summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/asmjit/src/asmjit/core/operand.h
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/asmjit/src/asmjit/core/operand.h')
-rw-r--r--3rdparty/asmjit/src/asmjit/core/operand.h2075
1 files changed, 1273 insertions, 802 deletions
diff --git a/3rdparty/asmjit/src/asmjit/core/operand.h b/3rdparty/asmjit/src/asmjit/core/operand.h
index 01cc1eaa560..8a1eee79b2f 100644
--- a/3rdparty/asmjit/src/asmjit/core/operand.h
+++ b/3rdparty/asmjit/src/asmjit/core/operand.h
@@ -1,225 +1,482 @@
-// AsmJit - Machine code generation for C++
+// This file is part of AsmJit project <https://asmjit.com>
//
-// * Official AsmJit Home Page: https://asmjit.com
-// * Official Github Repository: https://github.com/asmjit/asmjit
-//
-// Copyright (c) 2008-2020 The AsmJit Authors
-//
-// This software is provided 'as-is', without any express or implied
-// warranty. In no event will the authors be held liable for any damages
-// arising from the use of this software.
-//
-// Permission is granted to anyone to use this software for any purpose,
-// including commercial applications, and to alter it and redistribute it
-// freely, subject to the following restrictions:
-//
-// 1. The origin of this software must not be misrepresented; you must not
-// claim that you wrote the original software. If you use this software
-// in a product, an acknowledgment in the product documentation would be
-// appreciated but is not required.
-// 2. Altered source versions must be plainly marked as such, and must not be
-// misrepresented as being the original software.
-// 3. This notice may not be removed or altered from any source distribution.
+// See asmjit.h or LICENSE.md for license and copyright information
+// SPDX-License-Identifier: Zlib
#ifndef ASMJIT_CORE_OPERAND_H_INCLUDED
#define ASMJIT_CORE_OPERAND_H_INCLUDED
+#include "../core/archcommons.h"
#include "../core/support.h"
+#include "../core/type.h"
ASMJIT_BEGIN_NAMESPACE
-// ============================================================================
-// [Macros]
-// ============================================================================
-
-//! Adds a template specialization for `REG_TYPE` into the local `RegTraits`.
-#define ASMJIT_DEFINE_REG_TRAITS(REG, REG_TYPE, GROUP, SIZE, COUNT, TYPE_ID) \
-template<> \
-struct RegTraits<REG_TYPE> { \
- typedef REG RegT; \
- \
- static constexpr uint32_t kValid = 1; \
- static constexpr uint32_t kCount = COUNT; \
- static constexpr uint32_t kTypeId = TYPE_ID; \
- \
- static constexpr uint32_t kType = REG_TYPE; \
- static constexpr uint32_t kGroup = GROUP; \
- static constexpr uint32_t kSize = SIZE; \
- \
- static constexpr uint32_t kSignature = \
- (Operand::kOpReg << Operand::kSignatureOpShift ) | \
- (kType << Operand::kSignatureRegTypeShift ) | \
- (kGroup << Operand::kSignatureRegGroupShift) | \
- (kSize << Operand::kSignatureSizeShift ) ; \
-}
-
-//! Adds constructors and member functions to a class that implements abstract
-//! register. Abstract register is register that doesn't have type or signature
-//! yet, it's a base class like `x86::Reg` or `arm::Reg`.
-#define ASMJIT_DEFINE_ABSTRACT_REG(REG, BASE) \
-public: \
- /*! Default constructor that only setups basics. */ \
- constexpr REG() noexcept \
- : BASE(kSignature, kIdBad) {} \
- \
- /*! Makes a copy of the `other` register operand. */ \
- constexpr REG(const REG& other) noexcept \
- : BASE(other) {} \
- \
- /*! Makes a copy of the `other` register having id set to `rId` */ \
- constexpr REG(const BaseReg& other, uint32_t rId) noexcept \
- : BASE(other, rId) {} \
- \
- /*! Creates a register based on `signature` and `rId`. */ \
- constexpr REG(uint32_t signature, uint32_t rId) noexcept \
- : BASE(signature, rId) {} \
- \
- /*! Creates a completely uninitialized REG register operand (garbage). */ \
- inline explicit REG(Globals::NoInit_) noexcept \
- : BASE(Globals::NoInit) {} \
- \
- /*! Creates a new register from register type and id. */ \
- static inline REG fromTypeAndId(uint32_t rType, uint32_t rId) noexcept { \
- return REG(signatureOf(rType), rId); \
- } \
- \
- /*! Clones the register operand. */ \
- constexpr REG clone() const noexcept { return REG(*this); } \
- \
- inline REG& operator=(const REG& other) noexcept = default;
-
-//! Adds constructors and member functions to a class that implements final
-//! register. Final registers MUST HAVE a valid signature.
-#define ASMJIT_DEFINE_FINAL_REG(REG, BASE, TRAITS) \
-public: \
- static constexpr uint32_t kThisType = TRAITS::kType; \
- static constexpr uint32_t kThisGroup = TRAITS::kGroup; \
- static constexpr uint32_t kThisSize = TRAITS::kSize; \
- static constexpr uint32_t kSignature = TRAITS::kSignature; \
- \
- ASMJIT_DEFINE_ABSTRACT_REG(REG, BASE) \
- \
- /*! Creates a register operand having its id set to `rId`. */ \
- constexpr explicit REG(uint32_t rId) noexcept \
- : BASE(kSignature, rId) {}
-
//! \addtogroup asmjit_assembler
//! \{
-// ============================================================================
-// [asmjit::Operand_]
-// ============================================================================
+//! Operand type used by \ref Operand_.
+enum class OperandType : uint32_t {
+ //! Not an operand or not initialized.
+ kNone = 0,
+ //! Operand is a register.
+ kReg = 1,
+ //! Operand is a memory.
+ kMem = 2,
+ //! Operand is a register-list.
+ kRegList = 3,
+ //! Operand is an immediate value.
+ kImm = 4,
+ //! Operand is a label.
+ kLabel = 5,
+
+ //! Maximum value of `OperandType`.
+ kMaxValue = kRegList
+};
+
+static_assert(uint32_t(OperandType::kMem) == uint32_t(OperandType::kReg) + 1,
+ "AsmJit requires that `OperandType::kMem` equals to `OperandType::kReg + 1`");
-//! Constructor-less `Operand`.
-//!
-//! Contains no initialization code and can be used safely to define an array
-//! of operands that won't be initialized. This is an `Operand` compatible
-//! data structure designed to be statically initialized, static const, or to
-//! be used by the user to define an array of operands without having them
-//! default initialized.
-//!
-//! The key difference between `Operand` and `Operand_`:
+//! Register mask is a convenience typedef that describes a mask where each bit describes a physical register id
+//! in the same \ref RegGroup. At the moment 32 bits are enough as AsmJit doesn't support any architecture that
+//! would provide more than 32 registers for a register group.
+typedef uint32_t RegMask;
+
+//! Register type.
//!
-//! ```
-//! Operand_ xArray[10]; // Not initialized, contains garbage.
-//! Operand yArray[10]; // All operands initialized to none.
-//! ```
-struct Operand_ {
- //! Operand's signature that provides operand type and additional information.
- uint32_t _signature;
- //! Either base id as used by memory operand or any id as used by others.
- uint32_t _baseId;
+//! Provides a unique type that can be used to identify a register or its view.
+enum class RegType : uint8_t {
+ //! No register - unused, invalid, multiple meanings.
+ kNone = 0,
- //! Data specific to the operand type.
+ //! This is not a register type. This value is reserved for a \ref Label that's used in \ref BaseMem as a base.
//!
- //! The reason we don't use union is that we have `constexpr` constructors that
- //! construct operands and other `constexpr` functions that return wither another
- //! Operand or something else. These cannot generally work with unions so we also
- //! cannot use `union` if we want to be standard compliant.
- uint32_t _data[2];
+ //! Label tag is used as a sub-type, forming a unique signature across all operand types as 0x1 is never associated
+ //! with any register 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 (register type or label-tag) and operand id.
+ kLabelTag = 1,
+
+ //! Universal type describing program counter (PC) or instruction pointer (IP) register, if the target architecture
+ //! actually exposes it as a separate register type, which most modern architectures do.
+ kPC = 2,
+
+ //! 8-bit low general purpose register (X86).
+ kGp8Lo = 3,
+ //! 8-bit high general purpose register (X86).
+ kGp8Hi = 4,
+ //! 16-bit general purpose register (X86).
+ kGp16 = 5,
+ //! 32-bit general purpose register (X86|AArch32|AArch64).
+ kGp32 = 6,
+ //! 64-bit general purpose register (X86|AArch64).
+ kGp64 = 7,
+ //! 8-bit view of a vector register (AArch64).
+ kVec8 = 8,
+ //! 16-bit view of a vector register (AArch64).
+ kVec16 = 9,
+ //! 32-bit view of a vector register (AArch32|AArch64).
+ kVec32 = 10,
+ //! 64-bit view of a vector register (AArch32|AArch64).
+ //!
+ //! \note This is never used for MMX registers on X86, MMX registers have its own category.
+ kVec64 = 11,
+ //! 128-bit view of a vector register (X86|AArch32|AArch64).
+ kVec128 = 12,
+ //! 256-bit view of a vector register (X86).
+ kVec256 = 13,
+ //! 512-bit view of a vector register (X86).
+ kVec512 = 14,
+ //! 1024-bit view of a vector register (future).
+ kVec1024 = 15,
+ //! View of a vector register, which width is implementation specific (AArch64).
+ kVecNLen = 16,
+
+ //! Mask register (X86).
+ kMask = 17,
+
+ //! Start of architecture dependent register types.
+ kExtra = 18,
+
+ // X86 Specific Register Types
+ // ---------------------------
+
+ //! Instruction pointer (RIP), only addressable in \ref x86::Mem in 64-bit targets.
+ kX86_Rip = kPC,
+ //! Low GPB register (AL, BL, CL, DL, ...).
+ kX86_GpbLo = kGp8Lo,
+ //! High GPB register (AH, BH, CH, DH only).
+ kX86_GpbHi = kGp8Hi,
+ //! GPW register.
+ kX86_Gpw = kGp16,
+ //! GPD register.
+ kX86_Gpd = kGp32,
+ //! GPQ register (64-bit).
+ kX86_Gpq = kGp64,
+ //! XMM register (SSE+).
+ kX86_Xmm = kVec128,
+ //! YMM register (AVX+).
+ kX86_Ymm = kVec256,
+ //! ZMM register (AVX512+).
+ kX86_Zmm = kVec512,
+ //! K register (AVX512+).
+ kX86_KReg = kMask,
+ //! MMX register.
+ kX86_Mm = kExtra + 0,
+ //! Segment register (None, ES, CS, SS, DS, FS, GS).
+ kX86_SReg = kExtra + 1,
+ //! Control register (CR).
+ kX86_CReg = kExtra + 2,
+ //! Debug register (DR).
+ kX86_DReg = kExtra + 3,
+ //! FPU (x87) register.
+ kX86_St = kExtra + 4,
+ //! Bound register (BND).
+ kX86_Bnd = kExtra + 5,
+ //! TMM register (AMX_TILE)
+ kX86_Tmm = kExtra + 6,
+
+ // ARM Specific Register Types
+ // ---------------------------
+
+ //! Program pointer (PC) register (AArch64).
+ kARM_PC = kPC,
+ //! 32-bit general purpose register (R or W).
+ kARM_GpW = kGp32,
+ //! 64-bit general purpose register (X).
+ kARM_GpX = kGp64,
+ //! 8-bit view of VFP/ASIMD register (B).
+ kARM_VecB = kVec8,
+ //! 16-bit view of VFP/ASIMD register (H).
+ kARM_VecH = kVec16,
+ //! 32-bit view of VFP/ASIMD register (S).
+ kARM_VecS = kVec32,
+ //! 64-bit view of VFP/ASIMD register (D).
+ kARM_VecD = kVec64,
+ //! 128-bit view of VFP/ASIMD register (Q).
+ kARM_VecQ = kVec128,
+ //! 128-bit view of VFP/ASIMD register (V).
+ kARM_VecV = kVec128,
+
+ //! Maximum value of `RegType`.
+ kMaxValue = 31
+};
+ASMJIT_DEFINE_ENUM_COMPARE(RegType)
- //! Indexes to `_data` array.
- enum DataIndex : uint32_t {
- kDataMemIndexId = 0,
- kDataMemOffsetLo = 1,
+//! Register group.
+//!
+//! Provides a unique value that identifies groups of registers and their views.
+enum class RegGroup : uint8_t {
+ //! General purpose register group compatible with all backends.
+ kGp = 0,
+ //! Vector register group compatible with all backends.
+ //!
+ //! Describes X86 XMM|YMM|ZMM registers ARM/AArch64 V registers.
+ kVec = 1,
+
+ //! Mask register group compatible with all backends that can use masking.
+ kMask = 2,
+ //! Extra virtual group #3 that can be used by Compiler for register allocation.
+ kExtraVirt3 = 3,
+
+ //! Program counter group.
+ kPC = 4,
+
+ //! Extra non-virtual group that can be used by registers not managed by Compiler.
+ kExtraNonVirt = 5,
+
+ // X86 Specific Register Groups
+ // ----------------------------
+
+ //! K register group (KReg) - maps to \ref RegGroup::kMask (X86, X86_64).
+ kX86_K = kMask,
+ //! MMX register group (MM) - maps to \ref RegGroup::kExtraVirt3 (X86, X86_64).
+ kX86_MM = kExtraVirt3,
+
+ //! Instruction pointer (X86, X86_64).
+ kX86_Rip = kPC,
+ //! Segment register group (X86, X86_64).
+ kX86_SReg = kExtraNonVirt + 0,
+ //! CR register group (X86, X86_64).
+ kX86_CReg = kExtraNonVirt + 1,
+ //! DR register group (X86, X86_64).
+ kX86_DReg = kExtraNonVirt + 2,
+ //! FPU register group (X86, X86_64).
+ kX86_St = kExtraNonVirt + 3,
+ //! BND register group (X86, X86_64).
+ kX86_Bnd = kExtraNonVirt + 4,
+ //! TMM register group (X86, X86_64).
+ kX86_Tmm = kExtraNonVirt + 5,
+
+ //! First group - only used in loops.
+ k0 = 0,
+ //! Last value of a virtual register that is managed by \ref BaseCompiler.
+ kMaxVirt = Globals::kNumVirtGroups - 1,
+ //! Maximum value of `RegGroup`.
+ kMaxValue = 15
+};
+ASMJIT_DEFINE_ENUM_COMPARE(RegGroup)
- kDataImmValueLo = ASMJIT_ARCH_LE ? 0 : 1,
- kDataImmValueHi = ASMJIT_ARCH_LE ? 1 : 0
- };
+typedef Support::EnumValues<RegGroup, RegGroup::kGp, RegGroup::kMaxVirt> RegGroupVirtValues;
- //! Operand types that can be encoded in `Operand`.
- enum OpType : uint32_t {
- //! Not an operand or not initialized.
- kOpNone = 0,
- //! Operand is a register.
- kOpReg = 1,
- //! Operand is a memory.
- kOpMem = 2,
- //! Operand is an immediate value.
- kOpImm = 3,
- //! Operand is a label.
- kOpLabel = 4
- };
- static_assert(kOpMem == kOpReg + 1, "asmjit::Operand requires `kOpMem` to be `kOpReg+1`.");
-
- //! Label tag.
- enum LabelTag {
- //! Label tag is used as a sub-type, forming a unique signature across all
- //! operand types as 0x1 is never associated with any register 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
- //! (register type or label-tag) and operand id.
- kLabelTag = 0x1
- };
+//! Operand signature is a 32-bit number describing \ref Operand and some of its payload.
+//!
+//! In AsmJit operand signature is used to store additional payload of register, memory, and immediate operands.
+//! In practice the biggest pressure on OperandSignature is from \ref BaseMem and architecture specific memory
+//! operands that need to store additional payload that cannot be stored elsewhere as values of all other members
+//! are fully specified by \ref BaseMem.
+struct OperandSignature {
+ //! \name Constants
+ //! \{
- // \cond INTERNAL
- enum SignatureBits : uint32_t {
+ enum : uint32_t {
// Operand type (3 least significant bits).
// |........|........|........|.....XXX|
- kSignatureOpShift = 0,
- kSignatureOpMask = 0x07u << kSignatureOpShift,
+ kOpTypeShift = 0,
+ kOpTypeMask = 0x07u << kOpTypeShift,
// Register type (5 bits).
// |........|........|........|XXXXX...|
- kSignatureRegTypeShift = 3,
- kSignatureRegTypeMask = 0x1Fu << kSignatureRegTypeShift,
+ kRegTypeShift = 3,
+ kRegTypeMask = 0x1Fu << kRegTypeShift,
// Register group (4 bits).
// |........|........|....XXXX|........|
- kSignatureRegGroupShift = 8,
- kSignatureRegGroupMask = 0x0Fu << kSignatureRegGroupShift,
+ kRegGroupShift = 8,
+ kRegGroupMask = 0x0Fu << kRegGroupShift,
// Memory base type (5 bits).
// |........|........|........|XXXXX...|
- kSignatureMemBaseTypeShift = 3,
- kSignatureMemBaseTypeMask = 0x1Fu << kSignatureMemBaseTypeShift,
+ kMemBaseTypeShift = 3,
+ kMemBaseTypeMask = 0x1Fu << kMemBaseTypeShift,
// Memory index type (5 bits).
// |........|........|...XXXXX|........|
- kSignatureMemIndexTypeShift = 8,
- kSignatureMemIndexTypeMask = 0x1Fu << kSignatureMemIndexTypeShift,
+ kMemIndexTypeShift = 8,
+ kMemIndexTypeMask = 0x1Fu << kMemIndexTypeShift,
// Memory base+index combined (10 bits).
// |........|........|...XXXXX|XXXXX...|
- kSignatureMemBaseIndexShift = 3,
- kSignatureMemBaseIndexMask = 0x3FFu << kSignatureMemBaseIndexShift,
+ kMemBaseIndexShift = 3,
+ kMemBaseIndexMask = 0x3FFu << kMemBaseIndexShift,
+
+ // This memory operand represents a home-slot or stack (Compiler) (1 bit).
+ // |........|........|..X.....|........|
+ kMemRegHomeShift = 13,
+ kMemRegHomeFlag = 0x01u << kMemRegHomeShift,
- // Memory address type (2 bits).
- // |........|........|.XX.....|........|
- kSignatureMemAddrTypeShift = 13,
- kSignatureMemAddrTypeMask = 0x03u << kSignatureMemAddrTypeShift,
+ // Immediate type (1 bit).
+ // |........|........|........|....X...|
+ kImmTypeShift = 3,
+ kImmTypeMask = 0x01u << kImmTypeShift,
- // This memory operand represents a home-slot or stack (BaseCompiler).
- // |........|........|X.......|........|
- kSignatureMemRegHomeShift = 15,
- kSignatureMemRegHomeFlag = 0x01u << kSignatureMemRegHomeShift,
+ // Predicate used by either registers or immediate values (4 bits).
+ // |........|XXXX....|........|........|
+ kPredicateShift = 20,
+ kPredicateMask = 0x0Fu << kPredicateShift,
// Operand size (8 most significant bits).
// |XXXXXXXX|........|........|........|
- kSignatureSizeShift = 24,
- kSignatureSizeMask = 0xFFu << kSignatureSizeShift
+ kSizeShift = 24,
+ kSizeMask = 0xFFu << kSizeShift
+ };
+
+ //! \}
+
+ //! \name Members
+ //! \{
+
+ uint32_t _bits;
+
+ //! \}
+
+ //! \name Overloaded Operators
+ //!
+ //! Overloaded operators make `OperandSignature` behave like regular integer.
+ //!
+ //! \{
+
+ ASMJIT_INLINE_NODEBUG constexpr bool operator!() const noexcept { return _bits == 0; }
+ ASMJIT_INLINE_NODEBUG constexpr explicit operator bool() const noexcept { return _bits != 0; }
+
+ ASMJIT_INLINE_NODEBUG OperandSignature& operator|=(uint32_t x) noexcept { _bits |= x; return *this; }
+ ASMJIT_INLINE_NODEBUG OperandSignature& operator&=(uint32_t x) noexcept { _bits &= x; return *this; }
+ ASMJIT_INLINE_NODEBUG OperandSignature& operator^=(uint32_t x) noexcept { _bits ^= x; return *this; }
+
+ ASMJIT_INLINE_NODEBUG OperandSignature& operator|=(const OperandSignature& other) noexcept { return operator|=(other._bits); }
+ ASMJIT_INLINE_NODEBUG OperandSignature& operator&=(const OperandSignature& other) noexcept { return operator&=(other._bits); }
+ ASMJIT_INLINE_NODEBUG OperandSignature& operator^=(const OperandSignature& other) noexcept { return operator^=(other._bits); }
+
+ ASMJIT_INLINE_NODEBUG constexpr OperandSignature operator~() const noexcept { return OperandSignature{~_bits}; }
+
+ ASMJIT_INLINE_NODEBUG constexpr OperandSignature operator|(uint32_t x) const noexcept { return OperandSignature{_bits | x}; }
+ ASMJIT_INLINE_NODEBUG constexpr OperandSignature operator&(uint32_t x) const noexcept { return OperandSignature{_bits & x}; }
+ ASMJIT_INLINE_NODEBUG constexpr OperandSignature operator^(uint32_t x) const noexcept { return OperandSignature{_bits ^ x}; }
+
+ ASMJIT_INLINE_NODEBUG constexpr OperandSignature operator|(const OperandSignature& other) const noexcept { return OperandSignature{_bits | other._bits}; }
+ ASMJIT_INLINE_NODEBUG constexpr OperandSignature operator&(const OperandSignature& other) const noexcept { return OperandSignature{_bits & other._bits}; }
+ ASMJIT_INLINE_NODEBUG constexpr OperandSignature operator^(const OperandSignature& other) const noexcept { return OperandSignature{_bits ^ other._bits}; }
+
+ ASMJIT_INLINE_NODEBUG constexpr bool operator==(uint32_t x) const noexcept { return _bits == x; }
+ ASMJIT_INLINE_NODEBUG constexpr bool operator!=(uint32_t x) const noexcept { return _bits != x; }
+
+ ASMJIT_INLINE_NODEBUG constexpr bool operator==(const OperandSignature& other) const noexcept { return _bits == other._bits; }
+ ASMJIT_INLINE_NODEBUG constexpr bool operator!=(const OperandSignature& other) const noexcept { return _bits != other._bits; }
+
+ //! \}
+
+ //! \name Accessors
+ //! \{
+
+ ASMJIT_INLINE_NODEBUG void reset() noexcept { _bits = 0; }
+
+ ASMJIT_INLINE_NODEBUG constexpr uint32_t bits() const noexcept { return _bits; }
+ ASMJIT_INLINE_NODEBUG void setBits(uint32_t bits) noexcept { _bits = bits; }
+
+ template<uint32_t kFieldMask>
+ ASMJIT_INLINE_NODEBUG constexpr bool hasField() const noexcept {
+ return (_bits & kFieldMask) != 0;
+ }
+
+ template<uint32_t kFieldMask>
+ ASMJIT_INLINE_NODEBUG constexpr bool hasField(uint32_t value) const noexcept {
+ return (_bits & kFieldMask) != value << Support::ConstCTZ<kFieldMask>::value;
+ }
+
+ template<uint32_t kFieldMask>
+ ASMJIT_INLINE_NODEBUG constexpr uint32_t getField() const noexcept {
+ return (_bits >> Support::ConstCTZ<kFieldMask>::value) & (kFieldMask >> Support::ConstCTZ<kFieldMask>::value);
+ }
+
+ template<uint32_t kFieldMask>
+ ASMJIT_INLINE_NODEBUG void setField(uint32_t value) noexcept {
+ ASMJIT_ASSERT(((value << Support::ConstCTZ<kFieldMask>::value) & ~kFieldMask) == 0);
+ _bits = (_bits & ~kFieldMask) | (value << Support::ConstCTZ<kFieldMask>::value);
+ }
+
+ ASMJIT_INLINE_NODEBUG constexpr OperandSignature subset(uint32_t mask) const noexcept { return OperandSignature{_bits & mask}; }
+
+ template<uint32_t kFieldMask, uint32_t kFieldShift = Support::ConstCTZ<kFieldMask>::value>
+ ASMJIT_INLINE_NODEBUG constexpr OperandSignature replacedValue(uint32_t value) const noexcept { return OperandSignature{(_bits & ~kFieldMask) | (value << kFieldShift)}; }
+
+ template<uint32_t kFieldMask>
+ ASMJIT_INLINE_NODEBUG constexpr bool matchesSignature(const OperandSignature& signature) const noexcept {
+ return (_bits & kFieldMask) == signature._bits;
+ }
+
+ template<uint32_t kFieldMask>
+ ASMJIT_INLINE_NODEBUG constexpr bool matchesFields(uint32_t bits) const noexcept {
+ return (_bits & kFieldMask) == bits;
+ }
+
+ template<uint32_t kFieldMask>
+ ASMJIT_INLINE_NODEBUG constexpr bool matchesFields(const OperandSignature& fields) const noexcept {
+ return (_bits & kFieldMask) == fields._bits;
+ }
+
+ ASMJIT_INLINE_NODEBUG constexpr bool isValid() const noexcept { return _bits != 0; }
+
+ ASMJIT_INLINE_NODEBUG constexpr OperandType opType() const noexcept { return (OperandType)getField<kOpTypeMask>(); }
+
+ ASMJIT_INLINE_NODEBUG constexpr RegType regType() const noexcept { return (RegType)getField<kRegTypeMask>(); }
+ ASMJIT_INLINE_NODEBUG constexpr RegGroup regGroup() const noexcept { return (RegGroup)getField<kRegGroupMask>(); }
+
+ ASMJIT_INLINE_NODEBUG constexpr RegType memBaseType() const noexcept { return (RegType)getField<kMemBaseTypeMask>(); }
+ ASMJIT_INLINE_NODEBUG constexpr RegType memIndexType() const noexcept { return (RegType)getField<kMemIndexTypeMask>(); }
+
+ ASMJIT_INLINE_NODEBUG constexpr uint32_t predicate() const noexcept { return getField<kPredicateMask>(); }
+ ASMJIT_INLINE_NODEBUG constexpr uint32_t size() const noexcept { return getField<kSizeMask>(); }
+
+ ASMJIT_INLINE_NODEBUG void setOpType(OperandType opType) noexcept { setField<kOpTypeMask>(uint32_t(opType)); }
+ ASMJIT_INLINE_NODEBUG void setRegType(RegType regType) noexcept { setField<kRegTypeMask>(uint32_t(regType)); }
+ ASMJIT_INLINE_NODEBUG void setRegGroup(RegGroup regGroup) noexcept { setField<kRegGroupMask>(uint32_t(regGroup)); }
+
+ ASMJIT_INLINE_NODEBUG void setMemBaseType(RegType baseType) noexcept { setField<kMemBaseTypeMask>(uint32_t(baseType)); }
+ ASMJIT_INLINE_NODEBUG void setMemIndexType(RegType indexType) noexcept { setField<kMemIndexTypeMask>(uint32_t(indexType)); }
+
+ ASMJIT_INLINE_NODEBUG void setPredicate(uint32_t predicate) noexcept { setField<kPredicateMask>(predicate); }
+ ASMJIT_INLINE_NODEBUG void setSize(uint32_t size) noexcept { setField<kSizeMask>(size); }
+
+ //! \}
+
+ //! \name Static Constructors
+ //! \{
+
+ static ASMJIT_INLINE_NODEBUG constexpr OperandSignature fromBits(uint32_t bits) noexcept {
+ return OperandSignature{bits};
+ }
+
+ template<uint32_t kFieldMask, typename T>
+ static ASMJIT_INLINE_NODEBUG constexpr OperandSignature fromValue(const T& value) noexcept {
+ return OperandSignature{uint32_t(value) << Support::ConstCTZ<kFieldMask>::value};
+ }
+
+ static ASMJIT_INLINE_NODEBUG constexpr OperandSignature fromOpType(OperandType opType) noexcept {
+ return OperandSignature{uint32_t(opType) << kOpTypeShift};
+ }
+
+ static ASMJIT_INLINE_NODEBUG constexpr OperandSignature fromRegType(RegType regType) noexcept {
+ return OperandSignature{uint32_t(regType) << kRegTypeShift};
+ }
+
+ static ASMJIT_INLINE_NODEBUG constexpr OperandSignature fromRegGroup(RegGroup regGroup) noexcept {
+ return OperandSignature{uint32_t(regGroup) << kRegGroupShift};
+ }
+
+ static ASMJIT_INLINE_NODEBUG constexpr OperandSignature fromRegTypeAndGroup(RegType regType, RegGroup regGroup) noexcept {
+ return fromRegType(regType) | fromRegGroup(regGroup);
+ }
+
+ static ASMJIT_INLINE_NODEBUG constexpr OperandSignature fromMemBaseType(RegType baseType) noexcept {
+ return OperandSignature{uint32_t(baseType) << kMemBaseTypeShift};
+ }
+
+ static ASMJIT_INLINE_NODEBUG constexpr OperandSignature fromMemIndexType(RegType indexType) noexcept {
+ return OperandSignature{uint32_t(indexType) << kMemIndexTypeShift};
+ }
+
+ static ASMJIT_INLINE_NODEBUG constexpr OperandSignature fromPredicate(uint32_t predicate) noexcept {
+ return OperandSignature{predicate << kPredicateShift};
+ }
+
+ static ASMJIT_INLINE_NODEBUG constexpr OperandSignature fromSize(uint32_t size) noexcept {
+ return OperandSignature{size << kSizeShift};
+ }
+
+ //! \}
+};
+
+//! Base class representing an operand in AsmJit (non-default constructed version).
+//!
+//! 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 base structure designed to be statically initialized, static const, or to be used by user
+//! code to define an array of operands without having them default initialized at construction time.
+//!
+//! The key difference between \ref Operand and \ref Operand_ is:
+//!
+//! ```
+//! Operand_ xArray[10]; // Not initialized, contains garbage.
+//! Operand_ yArray[10] {}; // All operands initialized to none explicitly (zero initialized).
+//! Operand yArray[10]; // All operands initialized to none implicitly (zero initialized).
+//! ```
+struct Operand_ {
+ //! \name Types
+ //! \{
+
+ typedef OperandSignature Signature;
+
+ //! \}
+
+ //! \name Constants
+ //! \{
+
+ // Indexes to `_data` array.
+ enum DataIndex : uint32_t {
+ kDataMemIndexId = 0,
+ kDataMemOffsetLo = 1,
+
+ kDataImmValueLo = ASMJIT_ARCH_LE ? 0 : 1,
+ kDataImmValueHi = ASMJIT_ARCH_LE ? 1 : 0
};
- //! \endcond
//! Constants useful for VirtId <-> Index translation.
enum VirtIdConstants : uint32_t {
@@ -231,22 +488,40 @@ struct Operand_ {
kVirtIdCount = uint32_t(kVirtIdMax - kVirtIdMin + 1)
};
- //! Tests whether the given `id` is a valid virtual register id. Since AsmJit
- //! supports both physical and virtual registers it must be able to distinguish
- //! between these two. The idea is that physical registers are always limited
- //! in size, so virtual identifiers start from `kVirtIdMin` and end at `kVirtIdMax`.
- static ASMJIT_INLINE bool isVirtId(uint32_t id) noexcept { return id - kVirtIdMin < uint32_t(kVirtIdCount); }
+ //! \}
+
+ //! \name Members
+ //! \{
+
+ //! Provides operand type and additional payload.
+ Signature _signature;
+ //! Either base id as used by memory operand or any id as used by others.
+ uint32_t _baseId;
+
+ //! Data specific to the operand type.
+ //!
+ //! The reason we don't use union is that we have `constexpr` constructors that construct operands and other
+ //!`constexpr` functions that return whether another Operand or something else. These cannot generally work with
+ //! unions so we also cannot use `union` if we want to be standard compliant.
+ uint32_t _data[2];
+
+ //! \}
+
+ //! Tests whether the given `id` is a valid virtual register id. Since AsmJit supports both physical and virtual
+ //! registers it must be able to distinguish between these two. The idea is that physical registers are always
+ //! limited in size, so virtual identifiers start from `kVirtIdMin` and end at `kVirtIdMax`.
+ static ASMJIT_INLINE_NODEBUG bool isVirtId(uint32_t id) noexcept { return id - kVirtIdMin < uint32_t(kVirtIdCount); }
//! Converts a real-id into a packed-id that can be stored in Operand.
- static ASMJIT_INLINE uint32_t indexToVirtId(uint32_t id) noexcept { return id + kVirtIdMin; }
+ static ASMJIT_INLINE_NODEBUG uint32_t indexToVirtId(uint32_t id) noexcept { return id + kVirtIdMin; }
//! Converts a packed-id back to real-id.
- static ASMJIT_INLINE uint32_t virtIdToIndex(uint32_t id) noexcept { return id - kVirtIdMin; }
+ static ASMJIT_INLINE_NODEBUG uint32_t virtIdToIndex(uint32_t id) noexcept { return id - kVirtIdMin; }
//! \name Construction & Destruction
//! \{
//! \cond INTERNAL
//! Initializes a `BaseReg` operand from `signature` and register `id`.
- inline void _initReg(uint32_t signature, uint32_t id) noexcept {
+ ASMJIT_INLINE_NODEBUG void _initReg(const Signature& signature, uint32_t id) noexcept {
_signature = signature;
_baseId = id;
_data[0] = 0;
@@ -255,19 +530,23 @@ struct Operand_ {
//! \endcond
//! Initializes the operand from `other` operand (used by operator overloads).
- inline void copyFrom(const Operand_& other) noexcept { memcpy(this, &other, sizeof(Operand_)); }
+ ASMJIT_INLINE_NODEBUG void copyFrom(const Operand_& other) noexcept {
+ _signature._bits = other._signature._bits;
+ _baseId = other._baseId;
+ _data[0] = other._data[0];
+ _data[1] = other._data[1];
+ }
//! Resets the `Operand` to none.
//!
//! None operand is defined the following way:
- //! - Its signature is zero (kOpNone, and the rest zero as well).
+ //! - Its signature is zero (OperandType::kNone, 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()`.
+ //! 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;
@@ -285,8 +564,8 @@ struct Operand_ {
//! memset(&b, 0, sizeof(Operand));
//! assert(a == b);
//! ```
- inline void reset() noexcept {
- _signature = 0;
+ ASMJIT_INLINE_NODEBUG void reset() noexcept {
+ _signature.reset();
_baseId = 0;
_data[0] = 0;
_data[1] = 0;
@@ -294,13 +573,13 @@ struct Operand_ {
//! \}
- //! \name Operator Overloads
+ //! \name Overloaded Operators
//! \{
//! Tests whether this operand is the same as `other`.
- constexpr bool operator==(const Operand_& other) const noexcept { return equals(other); }
+ ASMJIT_INLINE_NODEBUG constexpr bool operator==(const Operand_& other) const noexcept { return equals(other); }
//! Tests whether this operand is not the same as `other`.
- constexpr bool operator!=(const Operand_& other) const noexcept { return !equals(other); }
+ ASMJIT_INLINE_NODEBUG constexpr bool operator!=(const Operand_& other) const noexcept { return !equals(other); }
//! \}
@@ -309,194 +588,227 @@ struct Operand_ {
//! Casts this operand to `T` type.
template<typename T>
- inline T& as() noexcept { return static_cast<T&>(*this); }
+ ASMJIT_INLINE_NODEBUG T& as() noexcept { return static_cast<T&>(*this); }
//! Casts this operand to `T` type (const).
template<typename T>
- inline const T& as() const noexcept { return static_cast<const T&>(*this); }
+ ASMJIT_INLINE_NODEBUG const T& as() const noexcept { return static_cast<const T&>(*this); }
+
+ //! \}
+
+ //! \name Equality
+ //! \{
+
+ //! Tests whether the operand is 100% equal to `other` operand.
+ //!
+ //! \note This basically performs a binary comparison, if aby bit is
+ //! different the operands are not equal.
+ ASMJIT_INLINE_NODEBUG constexpr bool equals(const Operand_& other) const noexcept {
+ return bool(unsigned(_signature == other._signature) &
+ unsigned(_baseId == other._baseId ) &
+ unsigned(_data[0] == other._data[0] ) &
+ unsigned(_data[1] == other._data[1] ));
+ }
//! \}
//! \name Accessors
//! \{
- //! Tests whether the operand matches the given signature `sign`.
- constexpr bool hasSignature(uint32_t signature) const noexcept { return _signature == signature; }
- //! Tests whether the operand matches the signature of the `other` operand.
- constexpr bool hasSignature(const Operand_& other) const noexcept { return _signature == other.signature(); }
+ //! Tests whether the operand's signature matches the signature of the `other` operand.
+ ASMJIT_INLINE_NODEBUG constexpr bool hasSignature(const Operand_& other) const noexcept { return _signature == other._signature; }
+ //! Tests whether the operand's signature matches the given signature `sign`.
+ ASMJIT_INLINE_NODEBUG constexpr bool hasSignature(const Signature& other) const noexcept { return _signature == other; }
//! Returns operand signature as unsigned 32-bit integer.
//!
- //! 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.
- constexpr uint32_t signature() const noexcept { return _signature; }
+ //! Signature is first 4 bytes of the operand data. It's used mostly for operand checking as it's
+ //! much faster to check packed 4 bytes at once than having to check these bytes individually.
+ ASMJIT_INLINE_NODEBUG constexpr Signature signature() const noexcept { return _signature; }
//! Sets the operand signature, see `signature()`.
//!
//! \note Improper use of `setSignature()` can lead to hard-to-debug errors.
- inline void setSignature(uint32_t signature) noexcept { _signature = signature; }
-
- //! \cond INTERNAL
- template<uint32_t mask>
- constexpr bool _hasSignaturePart() const noexcept {
- return (_signature & mask) != 0;
- }
-
- template<uint32_t mask>
- constexpr uint32_t _getSignaturePart() const noexcept {
- return (_signature >> Support::constCtz(mask)) & (mask >> Support::constCtz(mask));
- }
-
- template<uint32_t mask>
- inline void _setSignaturePart(uint32_t value) noexcept {
- ASMJIT_ASSERT((value & ~(mask >> Support::constCtz(mask))) == 0);
- _signature = (_signature & ~mask) | (value << Support::constCtz(mask));
- }
- //! \endcond
+ ASMJIT_INLINE_NODEBUG void setSignature(const Signature& signature) noexcept { _signature = signature; }
+ //! \overload
+ ASMJIT_INLINE_NODEBUG void setSignature(uint32_t signature) noexcept { _signature._bits = signature; }
//! Returns the type of the operand, see `OpType`.
- constexpr uint32_t opType() const noexcept { return _getSignaturePart<kSignatureOpMask>(); }
- //! Tests whether the operand is none (`kOpNone`).
- constexpr bool isNone() const noexcept { return _signature == 0; }
- //! Tests whether the operand is a register (`kOpReg`).
- constexpr bool isReg() const noexcept { return opType() == kOpReg; }
- //! Tests whether the operand is a memory location (`kOpMem`).
- constexpr bool isMem() const noexcept { return opType() == kOpMem; }
- //! Tests whether the operand is an immediate (`kOpImm`).
- constexpr bool isImm() const noexcept { return opType() == kOpImm; }
- //! Tests whether the operand is a label (`kOpLabel`).
- constexpr bool isLabel() const noexcept { return opType() == kOpLabel; }
+ ASMJIT_INLINE_NODEBUG constexpr OperandType opType() const noexcept { return _signature.opType(); }
+ //! Tests whether the operand is none (`OperandType::kNone`).
+ ASMJIT_INLINE_NODEBUG constexpr bool isNone() const noexcept { return _signature == Signature::fromBits(0); }
+ //! Tests whether the operand is a register (`OperandType::kReg`).
+ ASMJIT_INLINE_NODEBUG constexpr bool isReg() const noexcept { return opType() == OperandType::kReg; }
+ //! Tests whether the operand is a register-list.
+ //!
+ //! \note Register-list is currently only used by 32-bit ARM architecture.
+ ASMJIT_INLINE_NODEBUG constexpr bool isRegList() const noexcept { return opType() == OperandType::kRegList; }
+ //! Tests whether the operand is a memory location (`OperandType::kMem`).
+ ASMJIT_INLINE_NODEBUG constexpr bool isMem() const noexcept { return opType() == OperandType::kMem; }
+ //! Tests whether the operand is an immediate (`OperandType::kImm`).
+ ASMJIT_INLINE_NODEBUG constexpr bool isImm() const noexcept { return opType() == OperandType::kImm; }
+ //! Tests whether the operand is a label (`OperandType::kLabel`).
+ ASMJIT_INLINE_NODEBUG constexpr bool isLabel() const noexcept { return opType() == OperandType::kLabel; }
//! Tests whether the operand is a physical register.
- constexpr bool isPhysReg() const noexcept { return isReg() && _baseId < 0xFFu; }
+ ASMJIT_INLINE_NODEBUG constexpr bool isPhysReg() const noexcept { return isReg() && _baseId < 0xFFu; }
//! Tests whether the operand is a virtual register.
- constexpr bool isVirtReg() const noexcept { return isReg() && _baseId > 0xFFu; }
-
- //! Tests whether the operand specifies a size (i.e. the size is not zero).
- constexpr bool hasSize() const noexcept { return _hasSignaturePart<kSignatureSizeMask>(); }
- //! Tests whether the size of the operand matches `size`.
- constexpr bool hasSize(uint32_t s) const noexcept { return size() == s; }
-
- //! Returns the 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 `x86::CReg` and `x86::DReg`)
- //! 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.
- constexpr uint32_t size() const noexcept { return _getSignaturePart<kSignatureSizeMask>(); }
+ ASMJIT_INLINE_NODEBUG constexpr bool isVirtReg() const noexcept { return isReg() && _baseId > 0xFFu; }
//! Returns 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.
+ //! * 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
- //! `Globals::kInvalidId` if the label is invalid or not
- //! initialized.
- constexpr uint32_t id() const noexcept { return _baseId; }
+ //! * Label - Label id if it was created by using `newLabel()` or `Globals::kInvalidId` if the label is invalid or
+ //! not initialized.
+ ASMJIT_INLINE_NODEBUG constexpr uint32_t id() const noexcept { return _baseId; }
- //! Tests whether the operand is 100% equal to `other` operand.
- //!
- //! \note This basically performs a binary comparison, if aby bit is
- //! different the operands are not equal.
- constexpr bool equals(const Operand_& other) const noexcept {
- return (_signature == other._signature) &
- (_baseId == other._baseId ) &
- (_data[0] == other._data[0] ) &
- (_data[1] == other._data[1] ) ;
+ //! Tests whether the operand is a register matching the given register `type`.
+ ASMJIT_INLINE_NODEBUG constexpr bool isReg(RegType type) const noexcept {
+ return _signature.subset(Signature::kOpTypeMask | Signature::kRegTypeMask) == (Signature::fromOpType(OperandType::kReg) | Signature::fromRegType(type));
}
-#ifndef ASMJIT_NO_DEPRECATED
- ASMJIT_DEPRECATED("Use equals() instead")
- constexpr bool isEqual(const Operand_& other) const noexcept { return equals(other); }
-#endif //!ASMJIT_NO_DEPRECATED
-
- //! Tests whether the operand is a register matching `rType`.
- constexpr bool isReg(uint32_t rType) const noexcept {
- return (_signature & (kSignatureOpMask | kSignatureRegTypeMask)) ==
- ((kOpReg << kSignatureOpShift) | (rType << kSignatureRegTypeShift));
+ //! Tests whether the operand is a register of the provided register group `regGroup`.
+ ASMJIT_INLINE_NODEBUG constexpr bool isReg(RegGroup regGroup) const noexcept {
+ return _signature.subset(Signature::kOpTypeMask | Signature::kRegGroupMask) == (Signature::fromOpType(OperandType::kReg) | Signature::fromRegGroup(regGroup));
}
- //! Tests whether the operand is register and of `rType` and `rId`.
- constexpr bool isReg(uint32_t rType, uint32_t rId) const noexcept {
- return isReg(rType) && id() == rId;
+ //! Tests whether the operand is register and of register type `regType` and `regId`.
+ ASMJIT_INLINE_NODEBUG constexpr bool isReg(RegType regType, uint32_t regId) const noexcept { return isReg(regType) && _baseId == regId; }
+ //! Tests whether the operand is register and of register group `regGroup` and `regId`.
+ ASMJIT_INLINE_NODEBUG constexpr bool isReg(RegGroup regGroup, uint32_t regId) const noexcept { return isReg(regGroup) && _baseId == regId; }
+
+ //! Tests whether the register is a general purpose register (any size).
+ ASMJIT_INLINE_NODEBUG constexpr bool isGp() const noexcept { return isReg(RegGroup::kGp); }
+ //! Tests whether the register is a 32-bit general purpose register.
+ ASMJIT_INLINE_NODEBUG constexpr bool isGp32() const noexcept { return isReg(RegType::kGp32); }
+ //! Tests whether the register is a 64-bit general purpose register.
+ ASMJIT_INLINE_NODEBUG constexpr bool isGp64() const noexcept { return isReg(RegType::kGp64); }
+
+ //! Tests whether the register is a vector register of any size.
+ ASMJIT_INLINE_NODEBUG constexpr bool isVec() const noexcept { return isReg(RegGroup::kVec); }
+ //! Tests whether the register is an 8-bit vector register or view (AArch64).
+ ASMJIT_INLINE_NODEBUG constexpr bool isVec8() const noexcept { return isReg(RegType::kVec8); }
+ //! Tests whether the register is a 16-bit vector register or view (AArch64).
+ ASMJIT_INLINE_NODEBUG constexpr bool isVec16() const noexcept { return isReg(RegType::kVec16); }
+ //! Tests whether the register is a 32-bit vector register or view (AArch32, AArch64).
+ ASMJIT_INLINE_NODEBUG constexpr bool isVec32() const noexcept { return isReg(RegType::kVec32); }
+ //! Tests whether the register is a 64-bit vector register or view (AArch32, AArch64).
+ ASMJIT_INLINE_NODEBUG constexpr bool isVec64() const noexcept { return isReg(RegType::kVec64); }
+ //! Tests whether the register is a 128-bit vector register or view (AArch32, AArch64, X86, X86_64).
+ ASMJIT_INLINE_NODEBUG constexpr bool isVec128() const noexcept { return isReg(RegType::kVec128); }
+ //! Tests whether the register is a 256-bit vector register or view (X86, X86_64).
+ ASMJIT_INLINE_NODEBUG constexpr bool isVec256() const noexcept { return isReg(RegType::kVec256); }
+ //! Tests whether the register is a 512-bit vector register or view (X86, X86_64).
+ ASMJIT_INLINE_NODEBUG constexpr bool isVec512() const noexcept { return isReg(RegType::kVec512); }
+
+ //! Tests whether the register is a mask register of any size.
+ ASMJIT_INLINE_NODEBUG constexpr bool isMask() const noexcept { return isReg(RegGroup::kMask); }
+
+ //! Tests whether the operand is a register matching the given register `type`.
+ ASMJIT_INLINE_NODEBUG constexpr bool isRegList(RegType type) const noexcept {
+ return _signature.subset(Signature::kOpTypeMask | Signature::kRegTypeMask) == (Signature::fromOpType(OperandType::kRegList) | Signature::fromRegType(type));
}
//! Tests whether the operand is a register or memory.
- constexpr bool isRegOrMem() const noexcept {
- return Support::isBetween<uint32_t>(opType(), kOpReg, kOpMem);
+ //!
+ //! \note This is useful on X86 and X86_64 architectures as many instructions support Reg/Mem operand combination.
+ //! So if the user code works with just \ref Operand, it's possible to check whether the operand is either a register
+ //! or memory location with a single check.
+ ASMJIT_INLINE_NODEBUG constexpr bool isRegOrMem() const noexcept {
+ return Support::isBetween<uint32_t>(uint32_t(opType()), uint32_t(OperandType::kReg), uint32_t(OperandType::kMem));
+ }
+
+ //! Tests whether the operand is a register, register-list, or memory.
+ //!
+ //! \note This is useful on 32-bit ARM architecture to check whether an operand references a register. It can be
+ //! used in other architectures too, but it would work identically to \ref isRegOrMem() as other architectures
+ //! don't provide register lists.
+ ASMJIT_INLINE_NODEBUG constexpr bool isRegOrRegListOrMem() const noexcept {
+ return Support::isBetween<uint32_t>(uint32_t(opType()), uint32_t(OperandType::kReg), uint32_t(OperandType::kRegList));
}
//! \}
-};
-// ============================================================================
-// [asmjit::Operand]
-// ============================================================================
+ //! \name Accessors (X86 Specific)
+ //! \{
+
+ //! Returns a size of a register or an X86 memory operand.
+ //!
+ //! At the moment only X86 and X86_64 memory operands have a size - other memory operands can use bits that represent
+ //! size as an additional payload. This means that memory size is architecture specific and should be accessed via
+ //! \ref x86::Mem::size(). Sometimes when the user knows that the operand is either a register or memory operand this
+ //! function can be helpful as it avoids casting.
+ ASMJIT_INLINE_NODEBUG constexpr uint32_t x86RmSize() const noexcept {
+ return _signature.size();
+ }
+
+#if !defined(ASMJIT_NO_DEPRECATED)
+ ASMJIT_DEPRECATED("hasSize() is no longer portable - use x86RmSize() instead, if your target is X86/X86_64")
+ ASMJIT_INLINE_NODEBUG constexpr bool hasSize() const noexcept { return x86RmSize() != 0u; }
+
+ ASMJIT_DEPRECATED("hasSize() is no longer portable - use x86RmSize() instead, if your target is X86/X86_64")
+ ASMJIT_INLINE_NODEBUG constexpr bool hasSize(uint32_t s) const noexcept { return x86RmSize() == s; }
-//! Operand can contain register, memory location, immediate, or label.
+ ASMJIT_DEPRECATED("size() is no longer portable - use x86RmSize() instead, if your target is X86/X86_64")
+ ASMJIT_INLINE_NODEBUG constexpr uint32_t size() const noexcept { return _signature.getField<Signature::kSizeMask>(); }
+#endif
+
+ //! \}
+};
+
+//! Base class representing an operand in AsmJit (default constructed version).
class Operand : public Operand_ {
public:
//! \name Construction & Destruction
//! \{
//! Creates `kOpNone` operand having all members initialized to zero.
- constexpr Operand() noexcept
- : Operand_{ kOpNone, 0u, { 0u, 0u }} {}
+ ASMJIT_INLINE_NODEBUG constexpr Operand() noexcept
+ : Operand_{ Signature::fromOpType(OperandType::kNone), 0u, { 0u, 0u }} {}
//! Creates a cloned `other` operand.
- constexpr Operand(const Operand& other) noexcept = default;
+ ASMJIT_INLINE_NODEBUG constexpr Operand(const Operand& other) noexcept = default;
//! Creates a cloned `other` operand.
- constexpr explicit Operand(const Operand_& other)
+ ASMJIT_INLINE_NODEBUG constexpr explicit Operand(const Operand_& other)
: Operand_(other) {}
//! Creates an operand initialized to raw `[u0, u1, u2, u3]` values.
- constexpr Operand(Globals::Init_, uint32_t u0, uint32_t u1, uint32_t u2, uint32_t u3) noexcept
- : Operand_{ u0, u1, { u2, u3 }} {}
+ ASMJIT_INLINE_NODEBUG constexpr Operand(Globals::Init_, const Signature& u0, uint32_t u1, uint32_t u2, uint32_t u3) noexcept
+ : Operand_{{u0._bits}, u1, {u2, u3}} {}
//! Creates an uninitialized operand (dangerous).
- inline explicit Operand(Globals::NoInit_) noexcept {}
+ ASMJIT_INLINE_NODEBUG explicit Operand(Globals::NoInit_) noexcept {}
//! \}
- //! \name Operator Overloads
+ //! \name Overloaded Operators
//! \{
- inline Operand& operator=(const Operand& other) noexcept = default;
- inline Operand& operator=(const Operand_& other) noexcept { return operator=(static_cast<const Operand&>(other)); }
+ ASMJIT_INLINE_NODEBUG Operand& operator=(const Operand& other) noexcept = default;
+ ASMJIT_INLINE_NODEBUG Operand& operator=(const Operand_& other) noexcept { return operator=(static_cast<const Operand&>(other)); }
//! \}
- //! \name Utilities
+ //! \name Clone
//! \{
//! Clones this operand and returns its copy.
- constexpr Operand clone() const noexcept { return Operand(*this); }
+ ASMJIT_INLINE_NODEBUG constexpr Operand clone() const noexcept { return Operand(*this); }
//! \}
};
static_assert(sizeof(Operand) == 16, "asmjit::Operand must be exactly 16 bytes long");
-// ============================================================================
-// [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 BaseEmitter.
+//! 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 BaseEmitter.
//!
//! Example of using labels:
//!
@@ -519,39 +831,27 @@ static_assert(sizeof(Operand) == 16, "asmjit::Operand must be exactly 16 bytes l
//! ```
class Label : public Operand {
public:
- //! Type of the Label.
- enum LabelType : uint32_t {
- //! Anonymous (unnamed) label.
- kTypeAnonymous = 0,
- //! Local label (always has parentId).
- kTypeLocal = 1,
- //! Global label (never has parentId).
- kTypeGlobal = 2,
- //! Number of label types.
- kTypeCount = 3
- };
-
//! \name Construction & Destruction
//! \{
//! Creates a label operand without ID (you must set the ID to make it valid).
- constexpr Label() noexcept
- : Operand(Globals::Init, kOpLabel, Globals::kInvalidId, 0, 0) {}
+ ASMJIT_INLINE_NODEBUG constexpr Label() noexcept
+ : Operand(Globals::Init, Signature::fromOpType(OperandType::kLabel), Globals::kInvalidId, 0, 0) {}
//! Creates a cloned label operand of `other`.
- constexpr Label(const Label& other) noexcept
+ ASMJIT_INLINE_NODEBUG constexpr Label(const Label& other) noexcept
: Operand(other) {}
//! Creates a label operand of the given `id`.
- constexpr explicit Label(uint32_t id) noexcept
- : Operand(Globals::Init, kOpLabel, id, 0, 0) {}
+ ASMJIT_INLINE_NODEBUG constexpr explicit Label(uint32_t id) noexcept
+ : Operand(Globals::Init, Signature::fromOpType(OperandType::kLabel), id, 0, 0) {}
- inline explicit Label(Globals::NoInit_) noexcept
+ ASMJIT_INLINE_NODEBUG explicit Label(Globals::NoInit_) noexcept
: Operand(Globals::NoInit) {}
//! Resets the label, will reset all properties and set its ID to `Globals::kInvalidId`.
- inline void reset() noexcept {
- _signature = kOpLabel;
+ ASMJIT_INLINE_NODEBUG void reset() noexcept {
+ _signature = Signature::fromOpType(OperandType::kLabel);
_baseId = Globals::kInvalidId;
_data[0] = 0;
_data[1] = 0;
@@ -562,7 +862,7 @@ public:
//! \name Overloaded Operators
//! \{
- inline Label& operator=(const Label& other) noexcept = default;
+ ASMJIT_INLINE_NODEBUG Label& operator=(const Label& other) noexcept = default;
//! \}
@@ -570,157 +870,77 @@ public:
//! \{
//! Tests whether the label was created by CodeHolder and/or an attached emitter.
- constexpr bool isValid() const noexcept { return _baseId != Globals::kInvalidId; }
+ ASMJIT_INLINE_NODEBUG constexpr bool isValid() const noexcept { return _baseId != Globals::kInvalidId; }
//! Sets the label `id`.
- inline void setId(uint32_t id) noexcept { _baseId = id; }
+ ASMJIT_INLINE_NODEBUG void setId(uint32_t id) noexcept { _baseId = id; }
//! \}
};
-// ============================================================================
-// [asmjit::BaseRegTraits]
-// ============================================================================
-
//! \cond INTERNAL
//! Default register traits.
struct BaseRegTraits {
- //! RegType is not valid by default.
- static constexpr uint32_t kValid = 0;
- //! Count of registers (0 if none).
- static constexpr uint32_t kCount = 0;
- //! Everything is void by default.
- static constexpr uint32_t kTypeId = 0;
-
- //! Zero type by default.
- static constexpr uint32_t kType = 0;
- //! Zero group by default.
- static constexpr uint32_t kGroup = 0;
- //! No size by default.
- static constexpr uint32_t kSize = 0;
-
- //! Empty signature by default.
- static constexpr uint32_t kSignature = Operand::kOpReg;
+ enum : uint32_t {
+ //! \ref TypeId representing this register type, could be \ref TypeId::kVoid if such type doesn't exist.
+ kTypeId = uint32_t(TypeId::kVoid),
+ //! RegType is not valid by default.
+ kValid = 0,
+
+ //! Zero type by default (defaults to None).
+ kType = uint32_t(RegType::kNone),
+ //! Zero group by default (defaults to GP).
+ kGroup = uint32_t(RegGroup::kGp),
+ //! No size by default.
+ kSize = 0,
+
+ //! Empty signature by default (not even having operand type set to register).
+ kSignature = 0
+ };
};
//! \endcond
-// ============================================================================
-// [asmjit::BaseReg]
-// ============================================================================
-
-//! Structure that allows to extract a register information based on the signature.
-//!
-//! This information is compatible with operand's signature (32-bit integer)
-//! and `RegInfo` just provides easy way to access it.
-struct RegInfo {
- inline void reset(uint32_t signature = 0) noexcept { _signature = signature; }
- inline void setSignature(uint32_t signature) noexcept { _signature = signature; }
-
- template<uint32_t mask>
- constexpr uint32_t _getSignaturePart() const noexcept {
- return (_signature >> Support::constCtz(mask)) & (mask >> Support::constCtz(mask));
- }
-
- constexpr bool isValid() const noexcept { return _signature != 0; }
- constexpr uint32_t signature() const noexcept { return _signature; }
- constexpr uint32_t opType() const noexcept { return _getSignaturePart<Operand::kSignatureOpMask>(); }
- constexpr uint32_t group() const noexcept { return _getSignaturePart<Operand::kSignatureRegGroupMask>(); }
- constexpr uint32_t type() const noexcept { return _getSignaturePart<Operand::kSignatureRegTypeMask>(); }
- constexpr uint32_t size() const noexcept { return _getSignaturePart<Operand::kSignatureSizeMask>(); }
-
- uint32_t _signature;
-};
-
-//! Physical or virtual register operand.
+//! Physical or virtual register operand (base).
class BaseReg : 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.
- enum RegType : uint32_t {
- //! No register - unused, invalid, multiple meanings.
- kTypeNone = 0,
-
- // (1 is used as a LabelTag)
-
- //! 8-bit low general purpose register (X86).
- kTypeGp8Lo = 2,
- //! 8-bit high general purpose register (X86).
- kTypeGp8Hi = 3,
- //! 16-bit general purpose register (X86).
- kTypeGp16 = 4,
- //! 32-bit general purpose register (X86|ARM).
- kTypeGp32 = 5,
- //! 64-bit general purpose register (X86|ARM).
- kTypeGp64 = 6,
- //! 32-bit view of a vector register (ARM).
- kTypeVec32 = 7,
- //! 64-bit view of a vector register (ARM).
- kTypeVec64 = 8,
- //! 128-bit view of a vector register (X86|ARM).
- kTypeVec128 = 9,
- //! 256-bit view of a vector register (X86).
- kTypeVec256 = 10,
- //! 512-bit view of a vector register (X86).
- kTypeVec512 = 11,
- //! 1024-bit view of a vector register (future).
- kTypeVec1024 = 12,
- //! Other0 register, should match `kOther0` group.
- kTypeOther0 = 13,
- //! Other1 register, should match `kOther1` group.
- kTypeOther1 = 14,
- //! Universal id of IP/PC register (if separate).
- kTypeIP = 15,
- //! Start of platform dependent register types (must be honored).
- kTypeCustom = 16,
- //! Maximum possible register id of all architectures.
- kTypeMax = 31
- };
-
- //! Register group (architecture neutral), and some limits.
- enum RegGroup : uint32_t {
- //! General purpose register group compatible with all backends.
- kGroupGp = 0,
- //! Vector register group compatible with all backends.
- kGroupVec = 1,
- //! Group that is architecture dependent.
- kGroupOther0 = 2,
- //! Group that is architecture dependent.
- kGroupOther1 = 3,
- //! Count of register groups used by virtual registers.
- kGroupVirt = 4,
- //! Count of register groups used by physical registers.
- kGroupCount = 16
- };
+ //! \name Constants
+ //! \{
- enum Id : uint32_t {
+ enum : uint32_t {
//! None or any register (mostly internal).
- kIdBad = 0xFFu
+ kIdBad = 0xFFu,
+
+ kBaseSignatureMask =
+ Signature::kOpTypeMask |
+ Signature::kRegTypeMask |
+ Signature::kRegGroupMask |
+ Signature::kSizeMask,
+
+ kTypeNone = uint32_t(RegType::kNone),
+ kSignature = Signature::fromOpType(OperandType::kReg).bits()
};
- static constexpr uint32_t kSignature = kOpReg;
+ //! \}
//! \name Construction & Destruction
//! \{
//! Creates a dummy register operand.
- constexpr BaseReg() noexcept
- : Operand(Globals::Init, kSignature, kIdBad, 0, 0) {}
+ ASMJIT_INLINE_NODEBUG constexpr BaseReg() noexcept
+ : Operand(Globals::Init, Signature::fromOpType(OperandType::kReg), kIdBad, 0, 0) {}
//! Creates a new register operand which is the same as `other` .
- constexpr BaseReg(const BaseReg& other) noexcept
+ ASMJIT_INLINE_NODEBUG constexpr BaseReg(const BaseReg& other) noexcept
: Operand(other) {}
- //! Creates a new register operand compatible with `other`, but with a different `rId`.
- constexpr BaseReg(const BaseReg& other, uint32_t rId) noexcept
- : Operand(Globals::Init, other._signature, rId, 0, 0) {}
+ //! Creates a new register operand compatible with `other`, but with a different `id`.
+ ASMJIT_INLINE_NODEBUG constexpr BaseReg(const BaseReg& other, uint32_t id) noexcept
+ : Operand(Globals::Init, other._signature, id, 0, 0) {}
- //! Creates a register initialized to `signature` and `rId`.
- constexpr BaseReg(uint32_t signature, uint32_t rId) noexcept
- : Operand(Globals::Init, signature, rId, 0, 0) {}
+ //! Creates a register initialized to the given `signature` and `id`.
+ ASMJIT_INLINE_NODEBUG constexpr BaseReg(const Signature& signature, uint32_t id) noexcept
+ : Operand(Globals::Init, signature, id, 0, 0) {}
- inline explicit BaseReg(Globals::NoInit_) noexcept
+ ASMJIT_INLINE_NODEBUG explicit BaseReg(Globals::NoInit_) noexcept
: Operand(Globals::NoInit) {}
//! \}
@@ -728,82 +948,120 @@ public:
//! \name Overloaded Operators
//! \{
- inline BaseReg& operator=(const BaseReg& other) noexcept = default;
+ ASMJIT_INLINE_NODEBUG BaseReg& operator=(const BaseReg& other) noexcept = default;
//! \}
//! \name Accessors
//! \{
+ //! Returns base signature of the register associated with each register type.
+ //!
+ //! Base signature only contains the operand type, register type, register group, and register size. It doesn't
+ //! contain element type, predicate, or other architecture-specific data. Base signature is a signature that is
+ //! provided by architecture-specific `RegTraits`, like \ref x86::RegTraits.
+ ASMJIT_INLINE_NODEBUG constexpr OperandSignature baseSignature() const noexcept { return _signature & kBaseSignatureMask; }
+
+ //! Tests whether the operand's base signature matches the given signature `sign`.
+ ASMJIT_INLINE_NODEBUG constexpr bool hasBaseSignature(uint32_t signature) const noexcept { return baseSignature() == signature; }
+ //! Tests whether the operand's base signature matches the given signature `sign`.
+ ASMJIT_INLINE_NODEBUG constexpr bool hasBaseSignature(const OperandSignature& signature) const noexcept { return baseSignature() == signature; }
+ //! Tests whether the operand's base signature matches the base signature of the `other` operand.
+ ASMJIT_INLINE_NODEBUG constexpr bool hasBaseSignature(const BaseReg& other) const noexcept { return baseSignature() == other.baseSignature(); }
+
//! Tests whether this register is the same as `other`.
//!
- //! This is just an optimization. Registers by default only use the first
- //! 8 bytes of Operand data, so this method takes advantage of this knowledge
- //! and only compares these 8 bytes. If both operands were created correctly
- //! both \ref equals() and \ref isSame() should give the same answer, however,
- //! if any of these two contains garbage or other metadata in the upper 8
- //! bytes then \ref isSame() may return `true` in cases in which \ref equals()
+ //! This is just an optimization. Registers by default only use the first 8 bytes of Operand data, so this method
+ //! takes advantage of this knowledge and only compares these 8 bytes. If both operands were created correctly
+ //! both \ref equals() and \ref isSame() should give the same answer, however, if any of these two contains garbage
+ //! or other metadata in the upper 8 bytes then \ref isSame() may return `true` in cases in which \ref equals()
//! returns false.
- constexpr bool isSame(const BaseReg& other) const noexcept {
+ ASMJIT_INLINE_NODEBUG constexpr bool isSame(const BaseReg& other) const noexcept {
return (_signature == other._signature) & (_baseId == other._baseId);
}
//! Tests whether the register is valid (either virtual or physical).
- constexpr bool isValid() const noexcept { return (_signature != 0) & (_baseId != kIdBad); }
+ ASMJIT_INLINE_NODEBUG constexpr bool isValid() const noexcept { return bool(unsigned(_signature != 0) & unsigned(_baseId != kIdBad)); }
//! Tests whether this is a physical register.
- constexpr bool isPhysReg() const noexcept { return _baseId < kIdBad; }
+ ASMJIT_INLINE_NODEBUG constexpr bool isPhysReg() const noexcept { return _baseId < kIdBad; }
//! Tests whether this is a virtual register.
- constexpr bool isVirtReg() const noexcept { return _baseId > kIdBad; }
+ ASMJIT_INLINE_NODEBUG constexpr bool isVirtReg() const noexcept { return _baseId > kIdBad; }
//! Tests whether the register type matches `type` - same as `isReg(type)`, provided for convenience.
- constexpr bool isType(uint32_t type) const noexcept { return (_signature & kSignatureRegTypeMask) == (type << kSignatureRegTypeShift); }
+ ASMJIT_INLINE_NODEBUG constexpr bool isType(RegType type) const noexcept { return _signature.subset(Signature::kRegTypeMask) == Signature::fromRegType(type); }
//! Tests whether the register group matches `group`.
- constexpr bool isGroup(uint32_t group) const noexcept { return (_signature & kSignatureRegGroupMask) == (group << kSignatureRegGroupShift); }
+ ASMJIT_INLINE_NODEBUG constexpr bool isGroup(RegGroup group) const noexcept { return _signature.subset(Signature::kRegGroupMask) == Signature::fromRegGroup(group); }
//! Tests whether the register is a general purpose register (any size).
- constexpr bool isGp() const noexcept { return isGroup(kGroupGp); }
- //! Tests whether the register is a vector register.
- constexpr bool isVec() const noexcept { return isGroup(kGroupVec); }
+ ASMJIT_INLINE_NODEBUG constexpr bool isGp() const noexcept { return isGroup(RegGroup::kGp); }
+ //! Tests whether the register is a vector register of any size.
+ ASMJIT_INLINE_NODEBUG constexpr bool isVec() const noexcept { return isGroup(RegGroup::kVec); }
+ //! Tests whether the register is a mask register of any size.
+ ASMJIT_INLINE_NODEBUG constexpr bool isMask() const noexcept { return isGroup(RegGroup::kMask); }
using Operand_::isReg;
//! Same as `isType()`, provided for convenience.
- constexpr bool isReg(uint32_t rType) const noexcept { return isType(rType); }
- //! Tests whether the register type matches `type` and register id matches `rId`.
- constexpr bool isReg(uint32_t rType, uint32_t rId) const noexcept { return isType(rType) && id() == rId; }
+ ASMJIT_INLINE_NODEBUG constexpr bool isReg(RegType rType) const noexcept { return isType(rType); }
+ //! Tests whether the register type matches `type` and register id matches `id`.
+ ASMJIT_INLINE_NODEBUG constexpr bool isReg(RegType rType, uint32_t id) const noexcept { return isType(rType) && this->id() == id; }
- //! Returns the type of the register.
- constexpr uint32_t type() const noexcept { return _getSignaturePart<kSignatureRegTypeMask>(); }
+ //! Returns the register type.
+ ASMJIT_INLINE_NODEBUG constexpr RegType type() const noexcept { return _signature.regType(); }
//! Returns the register group.
- constexpr uint32_t group() const noexcept { return _getSignaturePart<kSignatureRegGroupMask>(); }
+ ASMJIT_INLINE_NODEBUG constexpr RegGroup group() const noexcept { return _signature.regGroup(); }
+
+ //! Tests whether the register specifies a size (i.e. the size is not zero).
+ ASMJIT_INLINE_NODEBUG constexpr bool hasSize() const noexcept { return _signature.hasField<Signature::kSizeMask>(); }
+ //! Tests whether the register size matches size `s`.
+ ASMJIT_INLINE_NODEBUG constexpr bool hasSize(uint32_t s) const noexcept { return size() == s; }
+
+ //! Returns the size of the register in bytes. If the register size depends on architecture (like `x86::CReg` and
+ //! `x86::DReg`) the size returned should be the greatest possible (so it should return 64-bit size in such case).
+ ASMJIT_INLINE_NODEBUG constexpr uint32_t size() const noexcept { return _signature.getField<Signature::kSizeMask>(); }
+
+ //! Returns operation predicate of the register (ARM/AArch64).
+ //!
+ //! The meaning depends on architecture, for example on ARM hardware this describes \ref arm::ShiftOp
+ //! of the register.
+ ASMJIT_INLINE_NODEBUG constexpr uint32_t predicate() const noexcept { return _signature.getField<Signature::kPredicateMask>(); }
+
+ //! Sets operation predicate of the register to `predicate` (ARM/AArch64).
+ //!
+ //! The meaning depends on architecture, for example on ARM hardware this describes \ref arm::ShiftOp
+ //! of the register.
+ ASMJIT_INLINE_NODEBUG void setPredicate(uint32_t predicate) noexcept { _signature.setField<Signature::kPredicateMask>(predicate); }
+
+ //! Resets shift operation type of the register to the default value (ARM/AArch64).
+ ASMJIT_INLINE_NODEBUG void resetPredicate() noexcept { _signature.setField<Signature::kPredicateMask>(0); }
//! Clones the register operand.
- constexpr BaseReg clone() const noexcept { return BaseReg(*this); }
+ ASMJIT_INLINE_NODEBUG constexpr BaseReg clone() const noexcept { return BaseReg(*this); }
//! Casts this register to `RegT` by also changing its signature.
//!
//! \note Improper use of `cloneAs()` can lead to hard-to-debug errors.
template<typename RegT>
- constexpr RegT cloneAs() const noexcept { return RegT(RegT::kSignature, id()); }
+ ASMJIT_INLINE_NODEBUG constexpr RegT cloneAs() const noexcept { return RegT(Signature(RegT::kSignature), id()); }
//! Casts this register to `other` by also changing its signature.
//!
//! \note Improper use of `cloneAs()` can lead to hard-to-debug errors.
template<typename RegT>
- constexpr RegT cloneAs(const RegT& other) const noexcept { return RegT(other.signature(), id()); }
+ ASMJIT_INLINE_NODEBUG constexpr RegT cloneAs(const RegT& other) const noexcept { return RegT(other.signature(), id()); }
- //! Sets the register id to `rId`.
- inline void setId(uint32_t rId) noexcept { _baseId = rId; }
+ //! Sets the register id to `id`.
+ ASMJIT_INLINE_NODEBUG void setId(uint32_t id) noexcept { _baseId = id; }
//! Sets a 32-bit operand signature based on traits of `RegT`.
template<typename RegT>
- inline void setSignatureT() noexcept { _signature = RegT::kSignature; }
+ ASMJIT_INLINE_NODEBUG void setSignatureT() noexcept { _signature = RegT::kSignature; }
- //! Sets the register `signature` and `rId`.
- inline void setSignatureAndId(uint32_t signature, uint32_t rId) noexcept {
+ //! Sets the register `signature` and `id`.
+ ASMJIT_INLINE_NODEBUG void setSignatureAndId(const OperandSignature& signature, uint32_t id) noexcept {
_signature = signature;
- _baseId = rId;
+ _baseId = id;
}
//! \}
@@ -812,41 +1070,38 @@ public:
//! \{
//! Tests whether the `op` operand is a general purpose register.
- static inline bool isGp(const Operand_& op) noexcept {
+ static ASMJIT_INLINE_NODEBUG bool isGp(const Operand_& op) noexcept {
// Check operand type and register group. Not interested in register type and size.
- const uint32_t kSgn = (kOpReg << kSignatureOpShift ) |
- (kGroupGp << kSignatureRegGroupShift) ;
- return (op.signature() & (kSignatureOpMask | kSignatureRegGroupMask)) == kSgn;
+ return op.signature().subset(Signature::kOpTypeMask | Signature::kRegGroupMask) == (Signature::fromOpType(OperandType::kReg) | Signature::fromRegGroup(RegGroup::kGp));
}
//! Tests whether the `op` operand is a vector register.
- static inline bool isVec(const Operand_& op) noexcept {
+ static ASMJIT_INLINE_NODEBUG bool isVec(const Operand_& op) noexcept {
// Check operand type and register group. Not interested in register type and size.
- const uint32_t kSgn = (kOpReg << kSignatureOpShift ) |
- (kGroupVec << kSignatureRegGroupShift) ;
- return (op.signature() & (kSignatureOpMask | kSignatureRegGroupMask)) == kSgn;
+ return op.signature().subset(Signature::kOpTypeMask | Signature::kRegGroupMask) == (Signature::fromOpType(OperandType::kReg) | Signature::fromRegGroup(RegGroup::kVec));
}
- //! Tests whether the `op` is a general purpose register of the given `rId`.
- static inline bool isGp(const Operand_& op, uint32_t rId) noexcept { return isGp(op) & (op.id() == rId); }
- //! Tests whether the `op` is a vector register of the given `rId`.
- static inline bool isVec(const Operand_& op, uint32_t rId) noexcept { return isVec(op) & (op.id() == rId); }
+ //! Tests whether the `op` is a general purpose register of the given `id`.
+ static ASMJIT_INLINE_NODEBUG bool isGp(const Operand_& op, uint32_t id) noexcept { return bool(unsigned(isGp(op)) & unsigned(op.id() == id)); }
+ //! Tests whether the `op` is a vector register of the given `id`.
+ static ASMJIT_INLINE_NODEBUG bool isVec(const Operand_& op, uint32_t id) noexcept { return bool(unsigned(isVec(op)) & unsigned(op.id() == id)); }
//! \}
};
-// ============================================================================
-// [asmjit::RegOnly]
-// ============================================================================
-
-//! RegOnly is 8-byte version of `BaseReg` that allows to store either register
-//! or nothing.
+//! RegOnly is 8-byte version of `BaseReg` that allows to store either register or nothing.
//!
-//! This class was designed to decrease the space consumed by each extra "operand"
-//! in `BaseEmitter` and `InstNode` classes.
+//! It's designed to decrease the space consumed by an extra "operand" in \ref BaseEmitter and \ref InstNode.
struct RegOnly {
- //! Type of the operand, either `kOpNone` or `kOpReg`.
- uint32_t _signature;
+ //! \name Types
+ //! \{
+
+ typedef OperandSignature Signature;
+
+ //! \}
+
+ //! Operand signature - only \ref OperandType::kNone and \ref OperandType::kReg are supported.
+ Signature _signature;
//! Physical or virtual register id.
uint32_t _id;
@@ -854,16 +1109,16 @@ struct RegOnly {
//! \{
//! Initializes the `RegOnly` instance to hold register `signature` and `id`.
- inline void init(uint32_t signature, uint32_t id) noexcept {
+ ASMJIT_INLINE_NODEBUG void init(const OperandSignature& signature, uint32_t id) noexcept {
_signature = signature;
_id = id;
}
- inline void init(const BaseReg& reg) noexcept { init(reg.signature(), reg.id()); }
- inline void init(const RegOnly& reg) noexcept { init(reg.signature(), reg.id()); }
+ ASMJIT_INLINE_NODEBUG void init(const BaseReg& reg) noexcept { init(reg.signature(), reg.id()); }
+ ASMJIT_INLINE_NODEBUG void init(const RegOnly& reg) noexcept { init(reg.signature(), reg.id()); }
//! Resets the `RegOnly` members to zeros (none).
- inline void reset() noexcept { init(0, 0); }
+ ASMJIT_INLINE_NODEBUG void reset() noexcept { init(Signature::fromBits(0), 0); }
//! \}
@@ -871,40 +1126,30 @@ struct RegOnly {
//! \{
//! Tests whether this ExtraReg is none (same as calling `Operand_::isNone()`).
- constexpr bool isNone() const noexcept { return _signature == 0; }
+ ASMJIT_INLINE_NODEBUG constexpr bool isNone() const noexcept { return _signature == 0; }
//! Tests whether the register is valid (either virtual or physical).
- constexpr bool isReg() const noexcept { return _signature != 0; }
+ ASMJIT_INLINE_NODEBUG constexpr bool isReg() const noexcept { return _signature != 0; }
//! Tests whether this is a physical register.
- constexpr bool isPhysReg() const noexcept { return _id < BaseReg::kIdBad; }
+ ASMJIT_INLINE_NODEBUG constexpr bool isPhysReg() const noexcept { return _id < BaseReg::kIdBad; }
//! Tests whether this is a virtual register (used by `BaseCompiler`).
- constexpr bool isVirtReg() const noexcept { return _id > BaseReg::kIdBad; }
+ ASMJIT_INLINE_NODEBUG constexpr bool isVirtReg() const noexcept { return _id > BaseReg::kIdBad; }
//! Returns the register signature or 0 if no register is assigned.
- constexpr uint32_t signature() const noexcept { return _signature; }
+ ASMJIT_INLINE_NODEBUG constexpr OperandSignature signature() const noexcept { return _signature; }
//! Returns the register id.
//!
- //! \note Always check whether the register is assigned before using the
- //! returned identifier as non-assigned `RegOnly` instance would return
- //! zero id, which is still a valid register id.
- constexpr uint32_t id() const noexcept { return _id; }
+ //! \note Always check whether the register is assigned before using the returned identifier as
+ //! non-assigned `RegOnly` instance would return zero id, which is still a valid register id.
+ ASMJIT_INLINE_NODEBUG constexpr uint32_t id() const noexcept { return _id; }
//! Sets the register id.
- inline void setId(uint32_t id) noexcept { _id = id; }
+ ASMJIT_INLINE_NODEBUG void setId(uint32_t id) noexcept { _id = id; }
- //! \cond INTERNAL
- //!
- //! Extracts information from operand's signature.
- template<uint32_t mask>
- constexpr uint32_t _getSignaturePart() const noexcept {
- return (_signature >> Support::constCtz(mask)) & (mask >> Support::constCtz(mask));
- }
- //! \endcond
-
- //! Returns the type of the register.
- constexpr uint32_t type() const noexcept { return _getSignaturePart<Operand::kSignatureRegTypeMask>(); }
+ //! Returns the register type.
+ ASMJIT_INLINE_NODEBUG constexpr RegType type() const noexcept { return _signature.regType(); }
//! Returns the register group.
- constexpr uint32_t group() const noexcept { return _getSignaturePart<Operand::kSignatureRegGroupMask>(); }
+ ASMJIT_INLINE_NODEBUG constexpr RegGroup group() const noexcept { return _signature.regGroup(); }
//! \}
@@ -913,109 +1158,326 @@ struct RegOnly {
//! Converts this ExtraReg to a real `RegT` operand.
template<typename RegT>
- constexpr RegT toReg() const noexcept { return RegT(_signature, _id); }
+ ASMJIT_INLINE_NODEBUG constexpr RegT toReg() const noexcept { return RegT(_signature, _id); }
//! \}
};
-// ============================================================================
-// [asmjit::BaseMem]
-// ============================================================================
+//! \cond INTERNAL
+//! Adds a template specialization for `REG_TYPE` into the local `RegTraits`.
+#define ASMJIT_DEFINE_REG_TRAITS(REG_TYPE, GROUP, SIZE, TYPE_ID) \
+template<> \
+struct RegTraits<REG_TYPE> { \
+ static constexpr uint32_t kValid = 1; \
+ static constexpr RegType kType = REG_TYPE; \
+ static constexpr RegGroup kGroup = GROUP; \
+ static constexpr uint32_t kSize = SIZE; \
+ static constexpr TypeId kTypeId = TYPE_ID; \
+ \
+ static constexpr uint32_t kSignature = \
+ (OperandSignature::fromOpType(OperandType::kReg) | \
+ OperandSignature::fromRegType(kType) | \
+ OperandSignature::fromRegGroup(kGroup) | \
+ OperandSignature::fromSize(kSize)).bits(); \
+ \
+}
+
+//! Adds constructors and member functions to a class that implements abstract register. Abstract register is register
+//! that doesn't have type or signature yet, it's a base class like `x86::Reg` or `arm::Reg`.
+#define ASMJIT_DEFINE_ABSTRACT_REG(REG, BASE) \
+public: \
+ /*! Default constructor that only setups basics. */ \
+ ASMJIT_INLINE_NODEBUG constexpr REG() noexcept \
+ : BASE(Signature{kSignature}, kIdBad) {} \
+ \
+ /*! Makes a copy of the `other` register operand. */ \
+ ASMJIT_INLINE_NODEBUG constexpr REG(const REG& other) noexcept \
+ : BASE(other) {} \
+ \
+ /*! Makes a copy of the `other` register having id set to `id` */ \
+ ASMJIT_INLINE_NODEBUG constexpr REG(const BaseReg& other, uint32_t id) noexcept \
+ : BASE(other, id) {} \
+ \
+ /*! Creates a register based on `signature` and `id`. */ \
+ ASMJIT_INLINE_NODEBUG constexpr REG(const OperandSignature& sgn, uint32_t id) noexcept \
+ : BASE(sgn, id) {} \
+ \
+ /*! Creates a completely uninitialized REG register operand (garbage). */ \
+ ASMJIT_INLINE_NODEBUG explicit REG(Globals::NoInit_) noexcept \
+ : BASE(Globals::NoInit) {} \
+ \
+ /*! Creates a new register from register type and id. */ \
+ static ASMJIT_INLINE_NODEBUG REG fromTypeAndId(RegType type, uint32_t id) noexcept { \
+ return REG(signatureOf(type), id); \
+ } \
+ \
+ /*! Clones the register operand. */ \
+ ASMJIT_INLINE_NODEBUG constexpr REG clone() const noexcept { return REG(*this); } \
+ \
+ ASMJIT_INLINE_NODEBUG REG& operator=(const REG& other) noexcept = default;
+
+//! Adds constructors and member functions to a class that implements final register. Final registers MUST HAVE a valid
+//! signature.
+#define ASMJIT_DEFINE_FINAL_REG(REG, BASE, TRAITS) \
+public: \
+ static constexpr RegType kThisType = TRAITS::kType; \
+ static constexpr RegGroup kThisGroup = TRAITS::kGroup; \
+ static constexpr uint32_t kThisSize = TRAITS::kSize; \
+ static constexpr uint32_t kSignature = TRAITS::kSignature; \
+ \
+ ASMJIT_DEFINE_ABSTRACT_REG(REG, BASE) \
+ \
+ /*! Creates a register operand having its id set to `id`. */ \
+ ASMJIT_INLINE_NODEBUG constexpr explicit REG(uint32_t id) noexcept \
+ : BASE(Signature{kSignature}, id) {}
+//! \endcond
+
+//! List of physical registers (base).
+//!
+//! \note List of registers is only used by some ARM instructions at the moment.
+class BaseRegList : public Operand {
+public:
+ //! \name Constants
+ //! \{
+
+ enum : uint32_t {
+ kSignature = Signature::fromOpType(OperandType::kRegList).bits()
+ };
+
+ //! \}
+
+ //! \name Construction & Destruction
+ //! \{
+
+ //! Creates a dummy register operand.
+ ASMJIT_INLINE_NODEBUG constexpr BaseRegList() noexcept
+ : Operand(Globals::Init, Signature::fromOpType(OperandType::kRegList), 0, 0, 0) {}
+
+ //! Creates a new register operand which is the same as `other` .
+ ASMJIT_INLINE_NODEBUG constexpr BaseRegList(const BaseRegList& other) noexcept
+ : Operand(other) {}
+
+ //! Creates a new register operand compatible with `other`, but with a different `id`.
+ ASMJIT_INLINE_NODEBUG constexpr BaseRegList(const BaseRegList& other, RegMask regMask) noexcept
+ : Operand(Globals::Init, other._signature, regMask, 0, 0) {}
+
+ //! Creates a register initialized to the given `signature` and `id`.
+ ASMJIT_INLINE_NODEBUG constexpr BaseRegList(const Signature& signature, RegMask regMask) noexcept
+ : Operand(Globals::Init, signature, regMask, 0, 0) {}
+
+ ASMJIT_INLINE_NODEBUG explicit BaseRegList(Globals::NoInit_) noexcept
+ : Operand(Globals::NoInit) {}
+
+ //! \}
+
+ //! \name Overloaded Operators
+ //! \{
+
+ ASMJIT_INLINE_NODEBUG BaseRegList& operator=(const BaseRegList& other) noexcept = default;
+
+ //! \}
+
+ //! \name Accessors
+ //! \{
+
+ //! Tests whether the register-list is valid, which means it has a type and at least a single register in the list.
+ ASMJIT_INLINE_NODEBUG constexpr bool isValid() const noexcept { return bool(unsigned(_signature != 0u) & unsigned(_baseId != 0u)); }
+
+ //! Tests whether the register type matches `type` - same as `isReg(type)`, provided for convenience.
+ ASMJIT_INLINE_NODEBUG constexpr bool isType(RegType type) const noexcept { return _signature.subset(Signature::kRegTypeMask) == Signature::fromRegType(type); }
+ //! Tests whether the register group matches `group`.
+ ASMJIT_INLINE_NODEBUG constexpr bool isGroup(RegGroup group) const noexcept { return _signature.subset(Signature::kRegGroupMask) == Signature::fromRegGroup(group); }
+
+ //! Tests whether the register is a general purpose register (any size).
+ ASMJIT_INLINE_NODEBUG constexpr bool isGp() const noexcept { return isGroup(RegGroup::kGp); }
+ //! Tests whether the register is a vector register.
+ ASMJIT_INLINE_NODEBUG constexpr bool isVec() const noexcept { return isGroup(RegGroup::kVec); }
+
+ //! Returns the register type.
+ ASMJIT_INLINE_NODEBUG constexpr RegType type() const noexcept { return _signature.regType(); }
+ //! Returns the register group.
+ ASMJIT_INLINE_NODEBUG constexpr RegGroup group() const noexcept { return _signature.regGroup(); }
+ //! Returns the size of a single register in this register-list or 0 if unspecified.
+ ASMJIT_INLINE_NODEBUG constexpr uint32_t size() const noexcept { return _signature.getField<Signature::kSizeMask>(); }
+
+ //! Returns the register list as a mask, where each bit represents one physical register.
+ ASMJIT_INLINE_NODEBUG constexpr RegMask list() const noexcept { return _baseId; }
+ //! Sets the register list to `mask`.
+ ASMJIT_INLINE_NODEBUG void setList(RegMask mask) noexcept { _baseId = mask; }
+ //! Remoes all registers from the register-list by making the underlying register-mask zero.
+ ASMJIT_INLINE_NODEBUG void resetList() noexcept { _baseId = 0; }
+
+ //! Adds registers passed by a register `mask` to the register-list.
+ ASMJIT_INLINE_NODEBUG void addList(RegMask mask) noexcept { _baseId |= mask; }
+ //! Removes registers passed by a register `mask` to the register-list.
+ ASMJIT_INLINE_NODEBUG void clearList(RegMask mask) noexcept { _baseId &= ~mask; }
+ //! Uses AND operator to combine the current register-list with other register `mask`.
+ ASMJIT_INLINE_NODEBUG void andList(RegMask mask) noexcept { _baseId &= mask; }
+ //! Uses XOR operator to combine the current register-list with other register `mask`.
+ ASMJIT_INLINE_NODEBUG void xorList(RegMask mask) noexcept { _baseId ^= mask; }
+
+ //! Checks whether a physical register `physId` is in the register-list.
+ ASMJIT_INLINE_NODEBUG bool hasReg(uint32_t physId) const noexcept { return physId < 32u ? (_baseId & (1u << physId)) != 0 : false; }
+ //! Adds a physical register `physId` to the register-list.
+ ASMJIT_INLINE_NODEBUG void addReg(uint32_t physId) noexcept { addList(1u << physId); }
+ //! Removes a physical register `physId` from the register-list.
+ ASMJIT_INLINE_NODEBUG void clearReg(uint32_t physId) noexcept { clearList(1u << physId); }
+
+ //! Clones the register-list operand.
+ ASMJIT_INLINE_NODEBUG constexpr BaseRegList clone() const noexcept { return BaseRegList(*this); }
+
+ //! Casts this register to `RegT` by also changing its signature.
+ //!
+ //! \note Improper use of `cloneAs()` can lead to hard-to-debug errors.
+ template<typename RegListT>
+ ASMJIT_INLINE_NODEBUG constexpr RegListT cloneAs() const noexcept { return RegListT(Signature(RegListT::kSignature), list()); }
+
+ //! Casts this register to `other` by also changing its signature.
+ //!
+ //! \note Improper use of `cloneAs()` can lead to hard-to-debug errors.
+ template<typename RegListT>
+ ASMJIT_INLINE_NODEBUG constexpr RegListT cloneAs(const RegListT& other) const noexcept { return RegListT(other.signature(), list()); }
+
+ //! \}
+};
+
+template<typename RegT>
+class RegListT : public BaseRegList {
+public:
+ //! \name Construction & Destruction
+ //! \{
+
+ //! Creates a dummy register operand.
+ ASMJIT_INLINE_NODEBUG constexpr RegListT() noexcept
+ : BaseRegList() {}
+
+ //! Creates a new register operand which is the same as `other` .
+ ASMJIT_INLINE_NODEBUG constexpr RegListT(const RegListT& other) noexcept
+ : BaseRegList(other) {}
+
+ //! Creates a new register operand compatible with `other`, but with a different `id`.
+ ASMJIT_INLINE_NODEBUG constexpr RegListT(const RegListT& other, RegMask regMask) noexcept
+ : BaseRegList(other, regMask) {}
+
+ //! Creates a register initialized to the given `signature` and `id`.
+ ASMJIT_INLINE_NODEBUG constexpr RegListT(const Signature& signature, RegMask regMask) noexcept
+ : BaseRegList(signature, regMask) {}
+
+ //! Creates a register initialized to the given `signature` and `regs`.
+ ASMJIT_INLINE_NODEBUG RegListT(const Signature& signature, std::initializer_list<RegT> regs) noexcept
+ : BaseRegList(signature, RegMask(0)) { addRegs(regs); }
+
+ ASMJIT_INLINE_NODEBUG explicit RegListT(Globals::NoInit_) noexcept
+ : BaseRegList(Globals::NoInit) {}
+
+ //! \}
+
+ //! \name Overloaded Operators
+ //! \{
+
+ ASMJIT_INLINE_NODEBUG RegListT& operator=(const RegListT& other) noexcept = default;
+
+ //! \}
+
+ //! \name Accessors
+ //! \{
+
+ using BaseRegList::addList;
+ using BaseRegList::clearList;
+ using BaseRegList::andList;
+ using BaseRegList::xorList;
+
+ //! Adds registers to this register-list as provided by `other` register-list.
+ ASMJIT_INLINE_NODEBUG void addList(const RegListT<RegT>& other) noexcept { addList(other.list()); }
+ //! Removes registers contained in `other` register-list.
+ ASMJIT_INLINE_NODEBUG void clearList(const RegListT<RegT>& other) noexcept { clearList(other.list()); }
+ //! Uses AND operator to combine the current register-list with `other` register-list.
+ ASMJIT_INLINE_NODEBUG void andList(const RegListT<RegT>& other) noexcept { andList(other.list()); }
+ //! Uses XOR operator to combine the current register-list with `other` register-list.
+ ASMJIT_INLINE_NODEBUG void xorList(const RegListT<RegT>& other) noexcept { xorList(other.list()); }
+
+ using BaseRegList::addReg;
+ using BaseRegList::clearReg;
+
+ ASMJIT_INLINE_NODEBUG void addReg(const RegT& reg) noexcept {
+ if (reg.id() < 32u)
+ addReg(reg.id());
+ }
+
+ ASMJIT_INLINE_NODEBUG void addRegs(std::initializer_list<RegT> regs) noexcept {
+ for (const RegT& reg : regs)
+ addReg(reg);
+ }
+
+ ASMJIT_INLINE_NODEBUG void clearReg(const RegT& reg) noexcept {
+ if (reg.id() < 32u)
+ clearReg(reg.id());
+ }
+
+ ASMJIT_INLINE_NODEBUG void clearRegs(std::initializer_list<RegT> regs) noexcept {
+ for (const RegT& reg : regs)
+ clearReg(reg);
+ }
+
+ //! \}
+};
//! 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 `BaseMem` splits data into the following parts:
+//! The data is split 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.
+//! - 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).
+//! - 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 32-bit id.
//!
-//! 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). In that case its low 32 bits are stored in
-//! DISPLACEMENT field and the remaining high 32 bits are stored in BASE.
+//! - 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). In that case its low 32 bits are stored in DISPLACEMENT field and the remaining high 32
+//! bits are stored in BASE.
//!
-//! OTHER - There is rest 8 bits that can be used for whatever purpose. The
-//! x86::Mem operand uses these bits to store segment override prefix and
-//! index shift (scale).
+//! - OTHER - There is rest 8 bits that can be used for whatever purpose. For example \ref x86::Mem operand uses
+//! these bits to store segment override prefix and index shift (or scale).
class BaseMem : public Operand {
public:
- //! Address type.
- enum AddrType : uint32_t {
- //! Default address type, Assembler will select the best type when necessary.
- kAddrTypeDefault = 0,
- //! Absolute address type.
- kAddrTypeAbs = 1,
- //! Relative address type.
- kAddrTypeRel = 2
- };
-
- // Shortcuts.
- enum SignatureMem : uint32_t {
- kSignatureMemAbs = kAddrTypeAbs << kSignatureMemAddrTypeShift,
- kSignatureMemRel = kAddrTypeRel << kSignatureMemAddrTypeShift
- };
-
- //! \cond INTERNAL
- //! Used internally to construct `BaseMem` operand from decomposed data.
- struct Decomposed {
- uint32_t baseType;
- uint32_t baseId;
- uint32_t indexType;
- uint32_t indexId;
- int32_t offset;
- uint32_t size;
- uint32_t flags;
- };
- //! \endcond
-
//! \name Construction & Destruction
//! \{
//! Creates a default `BaseMem` operand, that points to [0].
- constexpr BaseMem() noexcept
- : Operand(Globals::Init, kOpMem, 0, 0, 0) {}
+ ASMJIT_INLINE_NODEBUG constexpr BaseMem() noexcept
+ : Operand(Globals::Init, Signature::fromOpType(OperandType::kMem), 0, 0, 0) {}
//! Creates a `BaseMem` operand that is a clone of `other`.
- constexpr BaseMem(const BaseMem& other) noexcept
+ ASMJIT_INLINE_NODEBUG constexpr BaseMem(const BaseMem& other) noexcept
: Operand(other) {}
- //! \cond INTERNAL
-
- //! Creates a `BaseMem` operand from 4 integers as used by `Operand_` struct.
- constexpr BaseMem(Globals::Init_, uint32_t u0, uint32_t u1, uint32_t u2, uint32_t u3) noexcept
- : Operand(Globals::Init, u0, u1, u2, u3) {}
-
- constexpr BaseMem(const Decomposed& d) noexcept
+ //! Creates a `BaseMem` operand from `baseReg` and `offset`.
+ //!
+ //! \note This is an architecture independent constructor that can be used to create an architecture
+ //! independent memory operand to be used in portable code that can handle multiple architectures.
+ ASMJIT_INLINE_NODEBUG constexpr explicit BaseMem(const BaseReg& baseReg, int32_t offset = 0) noexcept
: Operand(Globals::Init,
- kOpMem | (d.baseType << kSignatureMemBaseTypeShift )
- | (d.indexType << kSignatureMemIndexTypeShift)
- | (d.size << kSignatureSizeShift )
- | d.flags,
- d.baseId,
- d.indexId,
- uint32_t(d.offset)) {}
+ Signature::fromOpType(OperandType::kMem) | Signature::fromMemBaseType(baseReg.type()),
+ baseReg.id(),
+ 0,
+ uint32_t(offset)) {}
+ //! \cond INTERNAL
+ //! Creates a `BaseMem` operand from 4 integers as used by `Operand_` struct.
+ ASMJIT_INLINE_NODEBUG constexpr BaseMem(const OperandSignature& u0, uint32_t baseId, uint32_t indexId, int32_t offset) noexcept
+ : Operand(Globals::Init, u0, baseId, indexId, uint32_t(offset)) {}
//! \endcond
//! Creates a completely uninitialized `BaseMem` operand.
- inline explicit BaseMem(Globals::NoInit_) noexcept
+ ASMJIT_INLINE_NODEBUG explicit BaseMem(Globals::NoInit_) noexcept
: Operand(Globals::NoInit) {}
//! Resets the memory operand - after the reset the memory points to [0].
- inline void reset() noexcept {
- _signature = kOpMem;
+ ASMJIT_INLINE_NODEBUG void reset() noexcept {
+ _signature = Signature::fromOpType(OperandType::kMem);
_baseId = 0;
_data[0] = 0;
_data[1] = 0;
@@ -1026,7 +1488,7 @@ public:
//! \name Overloaded Operators
//! \{
- inline BaseMem& operator=(const BaseMem& other) noexcept { copyFrom(other); return *this; }
+ ASMJIT_INLINE_NODEBUG BaseMem& operator=(const BaseMem& other) noexcept { copyFrom(other); return *this; }
//! \}
@@ -1034,136 +1496,142 @@ public:
//! \{
//! Clones the memory operand.
- constexpr BaseMem clone() const noexcept { return BaseMem(*this); }
+ ASMJIT_INLINE_NODEBUG constexpr BaseMem clone() const noexcept { return BaseMem(*this); }
- //! Returns the address type (see \ref AddrType) of the memory operand.
- //!
- //! By default, address type of newly created memory operands is always \ref kAddrTypeDefault.
- constexpr uint32_t addrType() const noexcept { return _getSignaturePart<kSignatureMemAddrTypeMask>(); }
- //! Sets the address type to `addrType`, see \ref AddrType.
- inline void setAddrType(uint32_t addrType) noexcept { _setSignaturePart<kSignatureMemAddrTypeMask>(addrType); }
- //! Resets the address type to \ref kAddrTypeDefault.
- inline void resetAddrType() noexcept { _setSignaturePart<kSignatureMemAddrTypeMask>(0); }
-
- //! Tests whether the address type is \ref kAddrTypeAbs.
- constexpr bool isAbs() const noexcept { return addrType() == kAddrTypeAbs; }
- //! Sets the address type to \ref kAddrTypeAbs.
- inline void setAbs() noexcept { setAddrType(kAddrTypeAbs); }
-
- //! Tests whether the address type is \ref kAddrTypeRel.
- constexpr bool isRel() const noexcept { return addrType() == kAddrTypeRel; }
- //! Sets the address type to \ref kAddrTypeRel.
- inline void setRel() noexcept { setAddrType(kAddrTypeRel); }
+ //! Creates a new copy of this memory operand adjusted by `off`.
+ ASMJIT_INLINE_NODEBUG BaseMem cloneAdjusted(int64_t off) const noexcept {
+ BaseMem result(*this);
+ result.addOffset(off);
+ return result;
+ }
//! Tests whether this memory operand is a register home (only used by \ref asmjit_compiler)
- constexpr bool isRegHome() const noexcept { return _hasSignaturePart<kSignatureMemRegHomeFlag>(); }
+ ASMJIT_INLINE_NODEBUG constexpr bool isRegHome() const noexcept { return _signature.hasField<Signature::kMemRegHomeFlag>(); }
//! Mark this memory operand as register home (only used by \ref asmjit_compiler).
- inline void setRegHome() noexcept { _signature |= kSignatureMemRegHomeFlag; }
+ ASMJIT_INLINE_NODEBUG void setRegHome() noexcept { _signature |= Signature::kMemRegHomeFlag; }
//! Marks this operand to not be a register home (only used by \ref asmjit_compiler).
- inline void clearRegHome() noexcept { _signature &= ~kSignatureMemRegHomeFlag; }
+ ASMJIT_INLINE_NODEBUG void clearRegHome() noexcept { _signature &= ~Signature::kMemRegHomeFlag; }
//! Tests whether the memory operand has a BASE register or label specified.
- constexpr bool hasBase() const noexcept { return (_signature & kSignatureMemBaseTypeMask) != 0; }
+ ASMJIT_INLINE_NODEBUG constexpr bool hasBase() const noexcept {
+ return (_signature & Signature::kMemBaseTypeMask) != 0;
+ }
+
//! Tests whether the memory operand has an INDEX register specified.
- constexpr bool hasIndex() const noexcept { return (_signature & kSignatureMemIndexTypeMask) != 0; }
+ ASMJIT_INLINE_NODEBUG constexpr bool hasIndex() const noexcept {
+ return (_signature & Signature::kMemIndexTypeMask) != 0;
+ }
+
//! Tests whether the memory operand has BASE or INDEX register.
- constexpr bool hasBaseOrIndex() const noexcept { return (_signature & kSignatureMemBaseIndexMask) != 0; }
+ ASMJIT_INLINE_NODEBUG constexpr bool hasBaseOrIndex() const noexcept {
+ return (_signature & Signature::kMemBaseIndexMask) != 0;
+ }
+
//! Tests whether the memory operand has BASE and INDEX register.
- constexpr bool hasBaseAndIndex() const noexcept { return (_signature & kSignatureMemBaseTypeMask) != 0 && (_signature & kSignatureMemIndexTypeMask) != 0; }
+ ASMJIT_INLINE_NODEBUG constexpr bool hasBaseAndIndex() const noexcept {
+ return (_signature & Signature::kMemBaseTypeMask) != 0 && (_signature & Signature::kMemIndexTypeMask) != 0;
+ }
- //! Tests whether the BASE operand is a register (registers start after `kLabelTag`).
- constexpr bool hasBaseReg() const noexcept { return (_signature & kSignatureMemBaseTypeMask) > (Label::kLabelTag << kSignatureMemBaseTypeShift); }
//! Tests whether the BASE operand is a label.
- constexpr bool hasBaseLabel() const noexcept { return (_signature & kSignatureMemBaseTypeMask) == (Label::kLabelTag << kSignatureMemBaseTypeShift); }
- //! Tests whether the INDEX operand is a register (registers start after `kLabelTag`).
- constexpr bool hasIndexReg() const noexcept { return (_signature & kSignatureMemIndexTypeMask) > (Label::kLabelTag << kSignatureMemIndexTypeShift); }
+ ASMJIT_INLINE_NODEBUG constexpr bool hasBaseLabel() const noexcept {
+ return _signature.subset(Signature::kMemBaseTypeMask) == Signature::fromMemBaseType(RegType::kLabelTag);
+ }
- //! Returns the type of the BASE register (0 if this memory operand doesn't
- //! use the BASE register).
+ //! Tests whether the BASE operand is a register (registers start after `RegType::kLabelTag`).
+ ASMJIT_INLINE_NODEBUG constexpr bool hasBaseReg() const noexcept {
+ return _signature.subset(Signature::kMemBaseTypeMask).bits() > Signature::fromMemBaseType(RegType::kLabelTag).bits();
+ }
+
+ //! Tests whether the INDEX operand is a register (registers start after `RegType::kLabelTag`).
+ ASMJIT_INLINE_NODEBUG constexpr bool hasIndexReg() const noexcept {
+ return _signature.subset(Signature::kMemIndexTypeMask).bits() > Signature::fromMemIndexType(RegType::kLabelTag).bits();
+ }
+
+ //! Returns the type of the 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 `baseId()` result.
- constexpr uint32_t baseType() const noexcept { return _getSignaturePart<kSignatureMemBaseTypeMask>(); }
+ //! \note If the returned type is one (a value never associated to a register type) the BASE is not register, but it
+ //! is a label. One equals to `kLabelTag`. You should always check `hasBaseLabel()` before using `baseId()` result.
+ ASMJIT_INLINE_NODEBUG constexpr RegType baseType() const noexcept { return _signature.memBaseType(); }
//! Returns the type of an INDEX register (0 if this memory operand doesn't
//! use the INDEX register).
- constexpr uint32_t indexType() const noexcept { return _getSignaturePart<kSignatureMemIndexTypeMask>(); }
+ ASMJIT_INLINE_NODEBUG constexpr RegType indexType() const noexcept { return _signature.memIndexType(); }
//! This is used internally for BASE+INDEX validation.
- constexpr uint32_t baseAndIndexTypes() const noexcept { return _getSignaturePart<kSignatureMemBaseIndexMask>(); }
+ ASMJIT_INLINE_NODEBUG constexpr uint32_t baseAndIndexTypes() const noexcept { return _signature.getField<Signature::kMemBaseIndexMask>(); }
- //! Returns both BASE (4:0 bits) and INDEX (9:5 bits) types combined into a
- //! single value.
+ //! Returns both BASE (4:0 bits) and INDEX (9:5 bits) types combined into a single value.
//!
- //! \remarks Returns id of the BASE register or label (if the BASE was
- //! specified as label).
- constexpr uint32_t baseId() const noexcept { return _baseId; }
+ //! \remarks Returns id of the BASE register or label (if the BASE was specified as label).
+ ASMJIT_INLINE_NODEBUG constexpr uint32_t baseId() const noexcept { return _baseId; }
//! Returns the id of the INDEX register.
- constexpr uint32_t indexId() const noexcept { return _data[kDataMemIndexId]; }
+ ASMJIT_INLINE_NODEBUG constexpr uint32_t indexId() const noexcept { return _data[kDataMemIndexId]; }
//! Sets the id of the BASE register (without modifying its type).
- inline void setBaseId(uint32_t rId) noexcept { _baseId = rId; }
+ ASMJIT_INLINE_NODEBUG void setBaseId(uint32_t id) noexcept { _baseId = id; }
+ //! Sets the register type of the BASE register (without modifying its id).
+ ASMJIT_INLINE_NODEBUG void setBaseType(RegType regType) noexcept { _signature.setMemBaseType(regType); }
+
//! Sets the id of the INDEX register (without modifying its type).
- inline void setIndexId(uint32_t rId) noexcept { _data[kDataMemIndexId] = rId; }
+ ASMJIT_INLINE_NODEBUG void setIndexId(uint32_t id) noexcept { _data[kDataMemIndexId] = id; }
+ //! Sets the register type of the INDEX register (without modifying its id).
+ ASMJIT_INLINE_NODEBUG void setIndexType(RegType regType) noexcept { _signature.setMemIndexType(regType); }
//! Sets the base register to type and id of the given `base` operand.
- inline void setBase(const BaseReg& base) noexcept { return _setBase(base.type(), base.id()); }
+ ASMJIT_INLINE_NODEBUG void setBase(const BaseReg& base) noexcept { return _setBase(base.type(), base.id()); }
//! Sets the index register to type and id of the given `index` operand.
- inline void setIndex(const BaseReg& index) noexcept { return _setIndex(index.type(), index.id()); }
+ ASMJIT_INLINE_NODEBUG void setIndex(const BaseReg& index) noexcept { return _setIndex(index.type(), index.id()); }
//! \cond INTERNAL
- inline void _setBase(uint32_t rType, uint32_t rId) noexcept {
- _setSignaturePart<kSignatureMemBaseTypeMask>(rType);
- _baseId = rId;
+ ASMJIT_INLINE_NODEBUG void _setBase(RegType type, uint32_t id) noexcept {
+ _signature.setField<Signature::kMemBaseTypeMask>(uint32_t(type));
+ _baseId = id;
}
- inline void _setIndex(uint32_t rType, uint32_t rId) noexcept {
- _setSignaturePart<kSignatureMemIndexTypeMask>(rType);
- _data[kDataMemIndexId] = rId;
+ ASMJIT_INLINE_NODEBUG void _setIndex(RegType type, uint32_t id) noexcept {
+ _signature.setField<Signature::kMemIndexTypeMask>(uint32_t(type));
+ _data[kDataMemIndexId] = id;
}
//! \endcond
//! Resets the memory operand's BASE register or label.
- inline void resetBase() noexcept { _setBase(0, 0); }
+ ASMJIT_INLINE_NODEBUG void resetBase() noexcept { _setBase(RegType::kNone, 0); }
//! Resets the memory operand's INDEX register.
- inline void resetIndex() noexcept { _setIndex(0, 0); }
+ ASMJIT_INLINE_NODEBUG void resetIndex() noexcept { _setIndex(RegType::kNone, 0); }
//! Sets the memory operand size (in bytes).
- inline void setSize(uint32_t size) noexcept { _setSignaturePart<kSignatureSizeMask>(size); }
+ ASMJIT_INLINE_NODEBUG void setSize(uint32_t size) noexcept { _signature.setField<Signature::kSizeMask>(size); }
//! Tests whether the memory operand has a 64-bit offset or absolute address.
//!
//! If this is true then `hasBase()` must always report false.
- constexpr bool isOffset64Bit() const noexcept { return baseType() == 0; }
+ ASMJIT_INLINE_NODEBUG constexpr bool isOffset64Bit() const noexcept { return baseType() == RegType::kNone; }
//! Tests whether the memory operand has a non-zero offset or absolute address.
- constexpr bool hasOffset() const noexcept {
+ ASMJIT_INLINE_NODEBUG constexpr bool hasOffset() const noexcept {
return (_data[kDataMemOffsetLo] | uint32_t(_baseId & Support::bitMaskFromBool<uint32_t>(isOffset64Bit()))) != 0;
}
//! Returns either relative offset or absolute address as 64-bit integer.
- constexpr int64_t offset() const noexcept {
+ ASMJIT_INLINE_NODEBUG constexpr int64_t offset() const noexcept {
return isOffset64Bit() ? int64_t(uint64_t(_data[kDataMemOffsetLo]) | (uint64_t(_baseId) << 32))
: int64_t(int32_t(_data[kDataMemOffsetLo])); // Sign extend 32-bit offset.
}
//! Returns a 32-bit low part of a 64-bit offset or absolute address.
- constexpr int32_t offsetLo32() const noexcept { return int32_t(_data[kDataMemOffsetLo]); }
+ ASMJIT_INLINE_NODEBUG constexpr int32_t offsetLo32() const noexcept { return int32_t(_data[kDataMemOffsetLo]); }
//! Returns a 32-but high part of a 64-bit offset or absolute address.
//!
//! \note This function is UNSAFE and returns garbage if `isOffset64Bit()`
//! returns false. Never use it blindly without checking it first.
- constexpr int32_t offsetHi32() const noexcept { return int32_t(_baseId); }
+ ASMJIT_INLINE_NODEBUG constexpr int32_t offsetHi32() const noexcept { return int32_t(_baseId); }
//! Sets 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.
+ //! \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.
inline void setOffset(int64_t offset) noexcept {
uint32_t lo = uint32_t(uint64_t(offset) & 0xFFFFFFFFu);
uint32_t hi = uint32_t(uint64_t(offset) >> 32);
@@ -1177,9 +1645,8 @@ public:
//! Adjusts the offset by `offset`.
//!
- //! \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.
+ //! \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.
//! Adjusts the memory operand offset by a `offset`.
inline void addOffset(int64_t offset) noexcept {
@@ -1193,50 +1660,89 @@ public:
}
}
- //! Adds `offset` to a low 32-bit offset part (don't use without knowing how
- //! BaseMem works).
- inline void addOffsetLo32(int32_t offset) noexcept { _data[kDataMemOffsetLo] += uint32_t(offset); }
+ //! Adds `offset` to a low 32-bit offset part (don't use without knowing how BaseMem works).
+ ASMJIT_INLINE_NODEBUG void addOffsetLo32(int32_t offset) noexcept { _data[kDataMemOffsetLo] += uint32_t(offset); }
//! Resets the memory offset to zero.
- inline void resetOffset() noexcept { setOffset(0); }
+ ASMJIT_INLINE_NODEBUG void resetOffset() noexcept { setOffset(0); }
- //! Resets the lo part of the memory offset to zero (don't use without knowing
- //! how BaseMem works).
- inline void resetOffsetLo32() noexcept { setOffsetLo32(0); }
+ //! Resets the lo part of the memory offset to zero (don't use without knowing how BaseMem works).
+ ASMJIT_INLINE_NODEBUG void resetOffsetLo32() noexcept { setOffsetLo32(0); }
//! \}
};
-// ============================================================================
-// [asmjit::Imm]
-// ============================================================================
+//! Type of the an immediate value.
+enum class ImmType : uint32_t {
+ //! Immediate is integer.
+ kInt = 0,
+ //! Immediate is a floating point stored as double-precision.
+ kDouble = 1
+};
-//! 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 an immediate operand use `asmjit::imm()` helper, which can be used
-//! with any type, not just the default 64-bit int.
+//! Immediate operands are encoded with instruction data.
class Imm : public Operand {
public:
+ //! \cond INTERNAL
+ template<typename T>
+ struct IsConstexprConstructibleAsImmType
+ : public std::integral_constant<bool, std::is_enum<T>::value ||
+ std::is_pointer<T>::value ||
+ std::is_integral<T>::value ||
+ std::is_function<T>::value> {};
+
+ template<typename T>
+ struct IsConvertibleToImmType
+ : public std::integral_constant<bool, IsConstexprConstructibleAsImmType<T>::value ||
+ std::is_floating_point<T>::value> {};
+ //! \endcond
+
//! \name Construction & Destruction
//! \{
//! Creates a new immediate value (initial value is 0).
- constexpr Imm() noexcept
- : Operand(Globals::Init, kOpImm, 0, 0, 0) {}
+ ASMJIT_INLINE_NODEBUG constexpr Imm() noexcept
+ : Operand(Globals::Init, Signature::fromOpType(OperandType::kImm), 0, 0, 0) {}
//! Creates a new immediate value from `other`.
- constexpr Imm(const Imm& other) noexcept
+ ASMJIT_INLINE_NODEBUG constexpr Imm(const Imm& other) noexcept
: Operand(other) {}
- //! Creates a new signed immediate value, assigning the value to `val`.
- constexpr explicit Imm(int64_t val) noexcept
- : Operand(Globals::Init, kOpImm, 0, Support::unpackU32At0(val), Support::unpackU32At1(val)) {}
+ //! Creates a new immediate value from ARM/AArch64 specific `shift`.
+ ASMJIT_INLINE_NODEBUG constexpr Imm(const arm::Shift& shift) noexcept
+ : Operand(Globals::Init,
+ Signature::fromOpType(OperandType::kImm) | Signature::fromPredicate(uint32_t(shift.op())),
+ 0,
+ Support::unpackU32At0(shift.value()),
+ Support::unpackU32At1(shift.value())) {}
+
+ //! Creates a new signed immediate value, assigning the value to `val` and an architecture-specific predicate
+ //! to `predicate`.
+ //!
+ //! \note Predicate is currently only used by ARM architectures.
+ template<typename T, typename = typename std::enable_if<IsConstexprConstructibleAsImmType<typename std::decay<T>::type>::value>::type>
+ ASMJIT_INLINE_NODEBUG constexpr Imm(const T& val, const uint32_t predicate = 0) noexcept
+ : Operand(Globals::Init,
+ Signature::fromOpType(OperandType::kImm) | Signature::fromPredicate(predicate),
+ 0,
+ Support::unpackU32At0(int64_t(val)),
+ Support::unpackU32At1(int64_t(val))) {}
+
+ ASMJIT_INLINE_NODEBUG Imm(const float& val, const uint32_t predicate = 0) noexcept
+ : Operand(Globals::Init,
+ Signature::fromOpType(OperandType::kImm) | Signature::fromPredicate(predicate),
+ 0,
+ 0,
+ 0) { setValue(val); }
+
+ ASMJIT_INLINE_NODEBUG Imm(const double& val, const uint32_t predicate = 0) noexcept
+ : Operand(Globals::Init,
+ Signature::fromOpType(OperandType::kImm) | Signature::fromPredicate(predicate),
+ 0,
+ 0,
+ 0) { setValue(val); }
- inline explicit Imm(Globals::NoInit_) noexcept
+ ASMJIT_INLINE_NODEBUG explicit Imm(Globals::NoInit_) noexcept
: Operand(Globals::NoInit) {}
//! \}
@@ -1245,60 +1751,84 @@ public:
//! \{
//! Assigns the value of the `other` operand to this immediate.
- inline Imm& operator=(const Imm& other) noexcept { copyFrom(other); return *this; }
+ ASMJIT_INLINE_NODEBUG Imm& operator=(const Imm& other) noexcept { copyFrom(other); return *this; }
//! \}
//! \name Accessors
//! \{
+ //! Returns immediate type.
+ ASMJIT_INLINE_NODEBUG constexpr ImmType type() const noexcept { return (ImmType)_signature.getField<Signature::kImmTypeMask>(); }
+ //! Sets the immediate type to `type`.
+ ASMJIT_INLINE_NODEBUG void setType(ImmType type) noexcept { _signature.setField<Signature::kImmTypeMask>(uint32_t(type)); }
+ //! Resets immediate type to \ref ImmType::kInt.
+ ASMJIT_INLINE_NODEBUG void resetType() noexcept { setType(ImmType::kInt); }
+
+ //! Returns operation predicate of the immediate.
+ //!
+ //! The meaning depends on architecture, for example on ARM hardware this describes \ref arm::ShiftOp
+ //! of the immediate.
+ ASMJIT_INLINE_NODEBUG constexpr uint32_t predicate() const noexcept { return _signature.getField<Signature::kPredicateMask>(); }
+
+ //! Sets operation predicate of the immediate to `predicate`.
+ //!
+ //! The meaning depends on architecture, for example on ARM hardware this describes \ref arm::ShiftOp
+ //! of the immediate.
+ ASMJIT_INLINE_NODEBUG void setPredicate(uint32_t predicate) noexcept { _signature.setField<Signature::kPredicateMask>(predicate); }
+
+ //! Resets the shift operation type of the immediate to the default value (no operation).
+ ASMJIT_INLINE_NODEBUG void resetPredicate() noexcept { _signature.setField<Signature::kPredicateMask>(0); }
+
//! Returns the immediate value as `int64_t`, which is the internal format Imm uses.
- constexpr int64_t value() const noexcept {
+ ASMJIT_INLINE_NODEBUG constexpr int64_t value() const noexcept {
return int64_t((uint64_t(_data[kDataImmValueHi]) << 32) | _data[kDataImmValueLo]);
}
+ //! Tests whether this immediate value is integer of any size.
+ ASMJIT_INLINE_NODEBUG constexpr uint32_t isInt() const noexcept { return type() == ImmType::kInt; }
+ //! Tests whether this immediate value is a double precision floating point value.
+ ASMJIT_INLINE_NODEBUG constexpr uint32_t isDouble() const noexcept { return type() == ImmType::kDouble; }
+
//! Tests whether the immediate can be casted to 8-bit signed integer.
- constexpr bool isInt8() const noexcept { return Support::isInt8(value()); }
+ ASMJIT_INLINE_NODEBUG constexpr bool isInt8() const noexcept { return type() == ImmType::kInt && Support::isInt8(value()); }
//! Tests whether the immediate can be casted to 8-bit unsigned integer.
- constexpr bool isUInt8() const noexcept { return Support::isUInt8(value()); }
+ ASMJIT_INLINE_NODEBUG constexpr bool isUInt8() const noexcept { return type() == ImmType::kInt && Support::isUInt8(value()); }
//! Tests whether the immediate can be casted to 16-bit signed integer.
- constexpr bool isInt16() const noexcept { return Support::isInt16(value()); }
+ ASMJIT_INLINE_NODEBUG constexpr bool isInt16() const noexcept { return type() == ImmType::kInt && Support::isInt16(value()); }
//! Tests whether the immediate can be casted to 16-bit unsigned integer.
- constexpr bool isUInt16() const noexcept { return Support::isUInt16(value()); }
+ ASMJIT_INLINE_NODEBUG constexpr bool isUInt16() const noexcept { return type() == ImmType::kInt && Support::isUInt16(value()); }
//! Tests whether the immediate can be casted to 32-bit signed integer.
- constexpr bool isInt32() const noexcept { return Support::isInt32(value()); }
+ ASMJIT_INLINE_NODEBUG constexpr bool isInt32() const noexcept { return type() == ImmType::kInt && Support::isInt32(value()); }
//! Tests whether the immediate can be casted to 32-bit unsigned integer.
- constexpr bool isUInt32() const noexcept { return _data[kDataImmValueHi] == 0; }
+ ASMJIT_INLINE_NODEBUG constexpr bool isUInt32() const noexcept { return type() == ImmType::kInt && _data[kDataImmValueHi] == 0; }
//! Returns the immediate value casted to `T`.
//!
- //! The value is masked before it's casted to `T` so the returned value is
- //! simply the representation of `T` considering the original value's lowest
- //! bits.
+ //! The value is masked before it's casted to `T` so the returned value is simply the representation of `T`
+ //! considering the original value's lowest bits.
template<typename T>
- constexpr T valueAs() const noexcept {
- return T(uint64_t(value()) & Support::allOnes<typename std::make_unsigned<T>::type>());
- }
+ ASMJIT_INLINE_NODEBUG T valueAs() const noexcept { return Support::immediateToT<T>(value()); }
//! Returns low 32-bit signed integer.
- constexpr int32_t int32Lo() const noexcept { return int32_t(_data[kDataImmValueLo]); }
+ ASMJIT_INLINE_NODEBUG constexpr int32_t int32Lo() const noexcept { return int32_t(_data[kDataImmValueLo]); }
//! Returns high 32-bit signed integer.
- constexpr int32_t int32Hi() const noexcept { return int32_t(_data[kDataImmValueHi]); }
+ ASMJIT_INLINE_NODEBUG constexpr int32_t int32Hi() const noexcept { return int32_t(_data[kDataImmValueHi]); }
//! Returns low 32-bit signed integer.
- constexpr uint32_t uint32Lo() const noexcept { return _data[kDataImmValueLo]; }
+ ASMJIT_INLINE_NODEBUG constexpr uint32_t uint32Lo() const noexcept { return _data[kDataImmValueLo]; }
//! Returns high 32-bit signed integer.
- constexpr uint32_t uint32Hi() const noexcept { return _data[kDataImmValueHi]; }
+ ASMJIT_INLINE_NODEBUG constexpr uint32_t uint32Hi() const noexcept { return _data[kDataImmValueHi]; }
//! Sets immediate value to `val`, the value is casted to a signed 64-bit integer.
template<typename T>
- inline void setValue(const T& val) noexcept {
- int64_t val64 = int64_t(Support::asNormalized(val));
- _data[kDataImmValueHi] = uint32_t(uint64_t(val64) >> 32);
- _data[kDataImmValueLo] = uint32_t(uint64_t(val64) & 0xFFFFFFFFu);
+ ASMJIT_INLINE_NODEBUG void setValue(const T& val) noexcept {
+ _setValueInternal(Support::immediateFromT(val), std::is_floating_point<T>::value ? ImmType::kDouble : ImmType::kInt);
}
- inline void setDouble(double d) noexcept {
- setValue(Support::bitCast<uint64_t>(d));
+ ASMJIT_INLINE_NODEBUG void _setValueInternal(int64_t val, ImmType type) noexcept {
+ setType(type);
+ _data[kDataImmValueHi] = uint32_t(uint64_t(val) >> 32);
+ _data[kDataImmValueLo] = uint32_t(uint64_t(val) & 0xFFFFFFFFu);
}
//! \}
@@ -1307,78 +1837,25 @@ public:
//! \{
//! Clones the immediate operand.
- constexpr Imm clone() const noexcept { return Imm(*this); }
+ ASMJIT_INLINE_NODEBUG constexpr Imm clone() const noexcept { return Imm(*this); }
- inline void signExtend8Bits() noexcept { setValue(int64_t(valueAs<int8_t>())); }
- inline void signExtend16Bits() noexcept { setValue(int64_t(valueAs<int16_t>())); }
- inline void signExtend32Bits() noexcept { setValue(int64_t(valueAs<int32_t>())); }
+ ASMJIT_INLINE_NODEBUG void signExtend8Bits() noexcept { setValue(int64_t(valueAs<int8_t>())); }
+ ASMJIT_INLINE_NODEBUG void signExtend16Bits() noexcept { setValue(int64_t(valueAs<int16_t>())); }
+ ASMJIT_INLINE_NODEBUG void signExtend32Bits() noexcept { setValue(int64_t(valueAs<int32_t>())); }
- inline void zeroExtend8Bits() noexcept { setValue(valueAs<uint8_t>()); }
- inline void zeroExtend16Bits() noexcept { setValue(valueAs<uint16_t>()); }
- inline void zeroExtend32Bits() noexcept { _data[kDataImmValueHi] = 0u; }
+ ASMJIT_INLINE_NODEBUG void zeroExtend8Bits() noexcept { setValue(valueAs<uint8_t>()); }
+ ASMJIT_INLINE_NODEBUG void zeroExtend16Bits() noexcept { setValue(valueAs<uint16_t>()); }
+ ASMJIT_INLINE_NODEBUG void zeroExtend32Bits() noexcept { _data[kDataImmValueHi] = 0u; }
//! \}
-
-#ifndef ASMJIT_NO_DEPRECATED
- ASMJIT_DEPRECATED("Use valueAs<int8_t>() instead")
- inline int8_t i8() const noexcept { return valueAs<int8_t>(); }
-
- ASMJIT_DEPRECATED("Use valueAs<uint8_t>() instead")
- inline uint8_t u8() const noexcept { return valueAs<uint8_t>(); }
-
- ASMJIT_DEPRECATED("Use valueAs<int16_t>() instead")
- inline int16_t i16() const noexcept { return valueAs<int16_t>(); }
-
- ASMJIT_DEPRECATED("Use valueAs<uint16_t>() instead")
- inline uint16_t u16() const noexcept { return valueAs<uint16_t>(); }
-
- ASMJIT_DEPRECATED("Use valueAs<int32_t>() instead")
- inline int32_t i32() const noexcept { return valueAs<int32_t>(); }
-
- ASMJIT_DEPRECATED("Use valueAs<uint32_t>() instead")
- inline uint32_t u32() const noexcept { return valueAs<uint32_t>(); }
-
- ASMJIT_DEPRECATED("Use value() instead")
- inline int64_t i64() const noexcept { return value(); }
-
- ASMJIT_DEPRECATED("Use valueAs<uint64_t>() instead")
- inline uint64_t u64() const noexcept { return valueAs<uint64_t>(); }
-
- ASMJIT_DEPRECATED("Use valueAs<intptr_t>() instead")
- inline intptr_t iptr() const noexcept { return valueAs<intptr_t>(); }
-
- ASMJIT_DEPRECATED("Use valueAs<uintptr_t>() instead")
- inline uintptr_t uptr() const noexcept { return valueAs<uintptr_t>(); }
-
- ASMJIT_DEPRECATED("Use int32Lo() instead")
- inline int32_t i32Lo() const noexcept { return int32Lo(); }
-
- ASMJIT_DEPRECATED("Use uint32Lo() instead")
- inline uint32_t u32Lo() const noexcept { return uint32Lo(); }
-
- ASMJIT_DEPRECATED("Use int32Hi() instead")
- inline int32_t i32Hi() const noexcept { return int32Hi(); }
-
- ASMJIT_DEPRECATED("Use uint32Hi() instead")
- inline uint32_t u32Hi() const noexcept { return uint32Hi(); }
-#endif // !ASMJIT_NO_DEPRECATED
};
//! Creates a new immediate operand.
-//!
-//! Using `imm(x)` is much nicer than using `Imm(x)` as this is a template
-//! which can accept any integer including pointers and function pointers.
template<typename T>
-static constexpr Imm imm(T val) noexcept {
- return Imm(std::is_signed<T>::value ? int64_t(val) : int64_t(uint64_t(val)));
-}
+static ASMJIT_INLINE_NODEBUG constexpr Imm imm(const T& val) noexcept { return Imm(val); }
//! \}
-// ============================================================================
-// [asmjit::Globals::none]
-// ============================================================================
-
namespace Globals {
//! \ingroup asmjit_assembler
//!
@@ -1386,31 +1863,25 @@ namespace Globals {
static constexpr const Operand none;
}
-// ============================================================================
-// [asmjit::Support::ForwardOp]
-// ============================================================================
-
//! \cond INTERNAL
namespace Support {
-template<typename T, bool IsIntegral>
+template<typename T, bool kIsImm>
struct ForwardOpImpl {
- static ASMJIT_INLINE const T& forward(const T& value) noexcept { return value; }
+ static ASMJIT_INLINE_NODEBUG const T& forward(const T& value) noexcept { return value; }
};
template<typename T>
struct ForwardOpImpl<T, true> {
- static ASMJIT_INLINE Imm forward(const T& value) noexcept { return Imm(value); }
+ static ASMJIT_INLINE_NODEBUG Imm forward(const T& value) noexcept { return Imm(value); }
};
-//! Either forwards operand T or returns a new operand for T if T is a type
-//! convertible to operand. At the moment this is only used to convert integers
-//! to \ref Imm operands.
+//! Either forwards operand T or returns a new operand that wraps it if T is a type convertible to operand.
+//! At the moment this is only used to convert integers, floats, and enumarations to \ref Imm operands.
template<typename T>
-struct ForwardOp : public ForwardOpImpl<T, std::is_integral<typename std::decay<T>::type>::value> {};
-
-}
+struct ForwardOp : public ForwardOpImpl<T, Imm::IsConvertibleToImmType<typename std::decay<T>::type>::value> {};
+} // {Support}
//! \endcond
ASMJIT_END_NAMESPACE