Files
asmjit/src/asmjit/ujit/ujitbase.h
kobalicek cdc4eacbb1 [abi] Added more functionality to ujit
* Renamed round to round_even
  * Added round_half_up intrinsic
  * Added floating-point mod
  * Added a scalar version of floating-point abs and neg
  * Added a behavior enum to specify how float to int conversion
    handles out-of-range and NaN cases
  * Updated some APX stuff in instruction database
2025-10-05 17:31:24 +02:00

735 lines
24 KiB
C++

// This file is part of AsmJit project <https://asmjit.com>
//
// See <asmjit/core.h> or LICENSE.md for license and copyright information
// SPDX-License-Identifier: Zlib
#ifndef ASMJIT_UJIT_JITBASE_P_H_INCLUDED
#define ASMJIT_UJIT_JITBASE_P_H_INCLUDED
#include "../host.h"
#if !defined(ASMJIT_NO_UJIT) && !defined(ASMJIT_HAS_HOST_BACKEND)
#pragma message("ASMJIT_NO_UJIT wasn't defined, however, no backends were found! (ASMJIT_HAS_HOST_BACKEND not defined)")
#endif
#if !defined(ASMJIT_NO_UJIT)
//! \namespace asmjit::ujit
//! \ingroup asmjit_ujit
//!
//! Namespace that provides all UJIT (Universal JIT) functionality.
ASMJIT_BEGIN_SUB_NAMESPACE(ujit)
//! \addtogroup asmjit_ujit
//! \{
//! Backend compiler is simply an alias to a `host::Compiler`, which would be used by \ref UniCompiler.
using BackendCompiler = host::Compiler;
//! Condition code is simply an alias to a `host::CondCode`.
using CondCode = host::CondCode;
//! Target memory operand.
using Mem = host::Mem;
//! Target general-purpose register.
using Gp = host::Gp;
//! Target vector register.
using Vec = host::Vec;
#if defined(ASMJIT_UJIT_X86)
static ASMJIT_INLINE_NODEBUG Mem mem_ptr(const Label& label, int32_t disp = 0) noexcept { return x86::ptr(label, disp); }
static ASMJIT_INLINE_NODEBUG Mem mem_ptr(const Gp& base, int32_t disp = 0) noexcept { return x86::ptr(base, disp); }
static ASMJIT_INLINE_NODEBUG Mem mem_ptr(const Gp& base, const Gp& index, uint32_t shift = 0, int32_t disp = 0) noexcept { return x86::ptr(base, index, shift, disp); }
#endif
#if defined(ASMJIT_UJIT_AARCH64)
static ASMJIT_INLINE_NODEBUG Mem mem_ptr(const Label& label, int32_t disp = 0) noexcept { return a64::ptr(label, disp); }
static ASMJIT_INLINE_NODEBUG Mem mem_ptr(const Gp& base, int32_t disp = 0) noexcept { return a64::ptr(base, disp); }
static ASMJIT_INLINE_NODEBUG Mem mem_ptr(const Gp& base, const Gp& index, uint32_t shift = 0) noexcept { return a64::ptr(base, index, a64::lsl(shift)); }
#endif
// Types & Enums
// -------------
//! Data alignment.
enum class Alignment : uint32_t {};
//! The behavior of a floating point scalar operation.
enum class ScalarOpBehavior : uint8_t {
//! The rest of the elements are zeroed, only the first element would contain the result (AArch64).
kZeroing,
//! The rest of the elements are unchanged, elements above 128-bits are zeroed.
kPreservingVec128
};
//! The behavior of floating point to int conversion.
enum class FloatToIntOutsideRangeBehavior : uint8_t {
//! In case that the floating point is outside of the integer range, the value is the smallest integer value,
//! which would be `0x80`, `0x8000`, `0x80000000`, or `0x8000000000000000` depending on the target integer width.
kSmallestValue,
//! In case that the floating point is outside of the integer range, the resulting integer will be saturated. If
//! the floating point is NaN, the resulting integer value would be zero.
kSaturatedValue
};
//! The behavior of a floating point min/max instructions when comparing against NaN.
enum class FMinFMaxOpBehavior : uint8_t {
//! Min and max selects a finite value if one of the compared values is NaN.
kFiniteValue,
//! Min and max is implemented like `if a <|> b ? a : b`.
kTernaryLogic
};
//! The behavior of floating point `madd` instructions.
enum class FMAddOpBehavior : uint8_t {
//! FMA is not available, thus `madd` is translated into two instructions (MUL + ADD).
kNoFMA,
//! FMA is available, the ISA allows to store the result to any of the inputs (X86|X86_64).
kFMAStoreToAny,
//! FMA is available, the ISA always uses accumulator register as a destination register (AArch64).
kFMAStoreToAccumulator
};
//! SIMD data width.
enum class DataWidth : uint8_t {
//! 8-bit elements.
k8 = 0,
//! 16-bit elements.
k16 = 1,
//! 32-bit elements.
k32 = 2,
//! 64-bit elements or 64-bit wide data is used.
k64 = 3,
//! 128-bit elements or 128-bit wide data is used.
k128 = 4
};
//! Vector register width.
enum class VecWidth : uint8_t {
//! 128-bit vector register (baseline, SSE/AVX, NEON, etc...).
k128 = 0,
//! 256-bit vector register (AVX2+).
k256 = 1,
//! 512-bit vector register (AVX512_DQ & AVX512_BW & AVX512_VL).
k512 = 2,
//! 1024-bit vector register (no backend at the moment).
k1024 = 3
};
//! Broadcast width.
enum class Bcst : uint8_t {
//! Broadcast 8-bit elements.
k8 = 0,
//! Broadcast 16-bit elements.
k16 = 1,
//! Broadcast 32-bit elements.
k32 = 2,
//! Broadcast 64-bit elements.
k64 = 3,
kNA = 0xFE,
kNA_Unique = 0xFF
};
namespace VecWidthUtils {
static ASMJIT_INLINE OperandSignature signature_of(VecWidth vw) noexcept {
RegType reg_type = RegType(uint32_t(RegType::kVec128) + uint32_t(vw));
uint32_t reg_size = 16u << uint32_t(vw);
return OperandSignature::from_op_type(OperandType::kReg) | OperandSignature::from_reg_type_and_group(reg_type, RegGroup::kVec) | OperandSignature::from_size(reg_size);
}
static ASMJIT_INLINE TypeId type_id_of(VecWidth vw) noexcept {
#if defined(ASMJIT_UJIT_X86)
static const TypeId table[] = {
TypeId::kInt32x4,
TypeId::kInt32x8,
TypeId::kInt32x16
};
return table[size_t(vw)];
#endif
#if defined(ASMJIT_UJIT_AARCH64)
Support::maybe_unused(vw);
return TypeId::kInt32x4;
#endif
}
static ASMJIT_INLINE_NODEBUG VecWidth vec_width_of(const Vec& reg) noexcept {
return VecWidth(uint32_t(reg.reg_type()) - uint32_t(RegType::kVec128));
}
static ASMJIT_INLINE_NODEBUG VecWidth vec_width_of(VecWidth vw, DataWidth data_width, uint32_t n) noexcept {
return VecWidth(Support::min<uint32_t>((n << uint32_t(data_width)) >> 5, uint32_t(vw)));
}
static ASMJIT_INLINE Vec clone_vec_as(const Vec& src, VecWidth vw) noexcept {
Vec result(src);
result.set_signature(signature_of(vw));
return result;
}
} // {VecWidthUtils}
// AsmJit Helpers
// ==============
//! Operand array, mostly used for code generation that uses SIMD.
//!
//! Can hold up to `kMaxSize` registers, however, the number of actual registers is dynamic and depends
//! on initialization.
class OpArray {
public:
using Op = Operand_;
//! Maximum number of active operands `OpArray` can hold.
static inline constexpr size_t kMaxSize = 8;
//! \name Members
//! \{
//! Number of operands in OpArray
size_t _size;
//! Underlying operand array.
Operand_ v[kMaxSize];
//! \}
//! \name Construction & Destruction
//! \{
ASMJIT_INLINE_NODEBUG OpArray() noexcept { reset(); }
ASMJIT_INLINE_NODEBUG explicit OpArray(const Op& op0) noexcept {
init(op0);
}
ASMJIT_INLINE_NODEBUG OpArray(const Op& op0, const Op& op1) noexcept {
init(op0, op1);
}
ASMJIT_INLINE_NODEBUG OpArray(const Op& op0, const Op& op1, const Op& op2) noexcept {
init(op0, op1, op2);
}
ASMJIT_INLINE_NODEBUG OpArray(const Op& op0, const Op& op1, const Op& op2, const Op& op3) noexcept {
init(op0, op1, op2, op3);
}
ASMJIT_INLINE_NODEBUG OpArray(const Op& op0, const Op& op1, const Op& op2, const Op& op3, const Op& op4) noexcept {
init(op0, op1, op2, op3, op4);
}
ASMJIT_INLINE_NODEBUG OpArray(const Op& op0, const Op& op1, const Op& op2, const Op& op3, const Op& op4, const Op& op5) noexcept {
init(op0, op1, op2, op3, op4, op5);
}
ASMJIT_INLINE_NODEBUG OpArray(const Op& op0, const Op& op1, const Op& op2, const Op& op3, const Op& op4, const Op& op5, const Op& op6) noexcept {
init(op0, op1, op2, op3, op4, op5, op6);
}
ASMJIT_INLINE_NODEBUG OpArray(const Op& op0, const Op& op1, const Op& op2, const Op& op3, const Op& op4, const Op& op5, const Op& op6, const Op& op7) noexcept {
init(op0, op1, op2, op3, op4, op5, op6, op7);
}
ASMJIT_INLINE_NODEBUG OpArray(const OpArray& other) noexcept { init(other); }
protected:
// Used internally to implement `low()`, `high()`, `even()`, and `odd()`.
ASMJIT_INLINE_NODEBUG OpArray(const OpArray& other, size_t from, size_t inc, size_t limit) noexcept {
size_t di = 0;
for (size_t si = from; si < limit; si += inc) {
v[di++] = other[si];
}
_size = di;
}
//! \}
protected:
ASMJIT_INLINE_NODEBUG OpArray& operator=(const OpArray& other) noexcept {
init(other);
return *this;
}
//! \name Internal Utilities
//! \{
ASMJIT_INLINE_NODEBUG void _resetFrom(size_t index) noexcept {
for (size_t i = index; i < kMaxSize; i++) {
v[index].reset();
}
}
//! \}
public:
//! Resets `OpArray` to a default construction state.
ASMJIT_INLINE_NODEBUG void reset() noexcept {
_size = 0;
for (size_t i = 0; i < kMaxSize; i++) {
v[i].reset();
}
}
ASMJIT_INLINE_NODEBUG void init(const Op* array, size_t size) noexcept {
_size = size;
if (size) {
memcpy(v, array, size * sizeof(Op));
}
_resetFrom(size);
}
ASMJIT_INLINE_NODEBUG void init(const OpArray& other) noexcept {
init(other.v, other._size);
}
ASMJIT_INLINE_NODEBUG void init(const Op& op0) noexcept {
_size = 1;
v[0] = op0;
_resetFrom(1);
}
ASMJIT_INLINE_NODEBUG void init(const Op& op0, const Op& op1) noexcept {
_size = 2;
v[0] = op0;
v[1] = op1;
_resetFrom(2);
}
ASMJIT_INLINE_NODEBUG void init(const Op& op0, const Op& op1, const Op& op2) noexcept {
_size = 3;
v[0] = op0;
v[1] = op1;
v[2] = op2;
_resetFrom(3);
}
ASMJIT_INLINE_NODEBUG void init(const Op& op0, const Op& op1, const Op& op2, const Op& op3) noexcept {
_size = 4;
v[0] = op0;
v[1] = op1;
v[2] = op2;
v[3] = op3;
_resetFrom(4);
}
ASMJIT_INLINE_NODEBUG void init(const Op& op0, const Op& op1, const Op& op2, const Op& op3, const Op& op4) noexcept {
_size = 5;
v[0] = op0;
v[1] = op1;
v[2] = op2;
v[3] = op3;
v[4] = op4;
_resetFrom(5);
}
ASMJIT_INLINE_NODEBUG void init(const Op& op0, const Op& op1, const Op& op2, const Op& op3, const Op& op4, const Op& op5) noexcept {
_size = 6;
v[0] = op0;
v[1] = op1;
v[2] = op2;
v[3] = op3;
v[4] = op4;
v[5] = op5;
_resetFrom(6);
}
ASMJIT_INLINE_NODEBUG void init(const Op& op0, const Op& op1, const Op& op2, const Op& op3, const Op& op4, const Op& op5, const Op& op6) noexcept {
_size = 7;
v[0] = op0;
v[1] = op1;
v[2] = op2;
v[3] = op3;
v[4] = op4;
v[5] = op5;
v[6] = op6;
_resetFrom(7);
}
ASMJIT_INLINE_NODEBUG void init(const Op& op0, const Op& op1, const Op& op2, const Op& op3, const Op& op4, const Op& op5, const Op& op6, const Op& op7) noexcept {
_size = 7;
v[0] = op0;
v[1] = op1;
v[2] = op2;
v[3] = op3;
v[4] = op4;
v[5] = op5;
v[6] = op6;
v[7] = op7;
}
//! Tests whether the vector is empty (has no elements).
[[nodiscard]]
ASMJIT_INLINE_NODEBUG bool is_empty() const noexcept { return _size == 0u; }
//! Tests whether the vector has only one element, which makes it scalar.
[[nodiscard]]
ASMJIT_INLINE_NODEBUG bool is_scalar() const noexcept { return _size == 1u; }
//! Tests whether the vector has more than 1 element, which means that calling `high()` and `odd()` won't return
//! an empty vector.
[[nodiscard]]
ASMJIT_INLINE_NODEBUG bool is_vector() const noexcept { return _size > 1u; }
//! Returns the number of vector elements.
[[nodiscard]]
ASMJIT_INLINE_NODEBUG size_t size() const noexcept { return _size; }
//! Returns the maximum size of vector elements.
[[nodiscard]]
ASMJIT_INLINE_NODEBUG size_t max_size() const noexcept { return kMaxSize; }
[[nodiscard]]
ASMJIT_INLINE bool equals(const OpArray& other) const noexcept {
size_t count = size();
if (count != other.size()) {
return false;
}
for (size_t i = 0; i < count; i++) {
if (v[i] != other.v[i]) {
return false;
}
}
return true;
}
[[nodiscard]]
ASMJIT_INLINE_NODEBUG bool operator==(const OpArray& other) const noexcept { return equals(other); }
[[nodiscard]]
ASMJIT_INLINE_NODEBUG bool operator!=(const OpArray& other) const noexcept { return !equals(other); }
[[nodiscard]]
ASMJIT_INLINE Operand_& operator[](size_t index) noexcept {
ASMJIT_ASSERT(index < _size);
return v[index];
}
[[nodiscard]]
ASMJIT_INLINE const Operand_& operator[](size_t index) const noexcept {
ASMJIT_ASSERT(index < _size);
return v[index];
}
[[nodiscard]]
ASMJIT_INLINE_NODEBUG OpArray lo() const noexcept { return OpArray(*this, 0u, 1u, (_size + 1u) / 2u); }
[[nodiscard]]
ASMJIT_INLINE_NODEBUG OpArray hi() const noexcept { return OpArray(*this, _size > 1u ? (_size + 1u) / 2u : size_t(0u), 1u, _size); }
[[nodiscard]]
ASMJIT_INLINE_NODEBUG OpArray even() const noexcept { return OpArray(*this, 0u, 2u, _size); }
[[nodiscard]]
ASMJIT_INLINE_NODEBUG OpArray odd() const noexcept { return OpArray(*this, _size > 1u, 2u, _size); }
[[nodiscard]]
ASMJIT_INLINE_NODEBUG OpArray half() const noexcept { return OpArray(*this, 0u, 1u, (_size + 1u) / 2u); }
[[nodiscard]]
ASMJIT_INLINE_NODEBUG OpArray every_nth(size_t n) const noexcept { return OpArray(*this, 0u, n, _size); }
//! Returns a new vector consisting of either even (from == 0) or odd (from == 1) elements. It's like
//! calling `even()` and `odd()`, but can be used within a loop that performs the same operation for both.
[[nodiscard]]
ASMJIT_INLINE_NODEBUG OpArray even_odd(size_t from) const noexcept { return OpArray(*this, _size > 1u ? from : size_t(0), 2u, _size); }
};
//! Vector operand array.
//!
//! Used to model SIMD code generation where the code generator can use up to \ref OpArray::kMaxSize registers per
//! `VecArray`. The advantage of `VecArray` is that it allows to parametrize the ideal number of registers at runtime
//! and to use a single code-path to generate advanced SIMD code.
//!
//! In addition, \ref UniCompiler fully understands `VecArray` so it can be passed instead of a regular operand when
//! emitting code, which greatly simplifies designing high-performance SIMD code.
//!
//! \note VecArray is like \ref OpArray, just the whole API works with \ref Vec instead of \ref Operand_.
class VecArray : public OpArray {
public:
//! \name Construction & Destruction
//! \{
ASMJIT_INLINE_NODEBUG VecArray() noexcept
: OpArray() {}
ASMJIT_INLINE_NODEBUG VecArray(const VecArray& other) noexcept
: OpArray(other) {}
ASMJIT_INLINE_NODEBUG explicit VecArray(const Vec& op0) noexcept
: OpArray(op0) {}
ASMJIT_INLINE_NODEBUG VecArray(const Vec& op0, const Vec& op1) noexcept
: OpArray(op0, op1) {}
ASMJIT_INLINE_NODEBUG VecArray(const Vec& op0, const Vec& op1, const Vec& op2) noexcept
: OpArray(op0, op1, op2) {}
ASMJIT_INLINE_NODEBUG VecArray(const Vec& op0, const Vec& op1, const Vec& op2, const Vec& op3) noexcept
: OpArray(op0, op1, op2, op3) {}
ASMJIT_INLINE_NODEBUG VecArray(const Vec& op0, const Vec& op1, const Vec& op2, const Vec& op3, const Vec& op4) noexcept
: OpArray(op0, op1, op2, op3, op4) {}
ASMJIT_INLINE_NODEBUG VecArray(const Vec& op0, const Vec& op1, const Vec& op2, const Vec& op3, const Vec& op4, const Vec& op5) noexcept
: OpArray(op0, op1, op2, op3, op4, op5) {}
ASMJIT_INLINE_NODEBUG VecArray(const Vec& op0, const Vec& op1, const Vec& op2, const Vec& op3, const Vec& op4, const Vec& op5, const Vec& op6) noexcept
: OpArray(op0, op1, op2, op3, op4, op5, op6) {}
ASMJIT_INLINE_NODEBUG VecArray(const Vec& op0, const Vec& op1, const Vec& op2, const Vec& op3, const Vec& op4, const Vec& op5, const Vec& op6, const Vec& op7) noexcept
: OpArray(op0, op1, op2, op3, op4, op5, op6, op7) {}
protected:
ASMJIT_INLINE_NODEBUG VecArray(const VecArray& other, size_t from, size_t inc, size_t limit) noexcept
: OpArray(other, from, inc, limit) {}
//! \}
//! \name Overloaded Operators
//! \{
public:
ASMJIT_INLINE_NODEBUG VecArray& operator=(const VecArray& other) noexcept {
init(other);
return *this;
}
ASMJIT_INLINE Vec& operator[](size_t index) noexcept {
ASMJIT_ASSERT(index < _size);
return static_cast<Vec&>(v[index]);
}
ASMJIT_INLINE const Vec& operator[](size_t index) const noexcept {
ASMJIT_ASSERT(index < _size);
return static_cast<const Vec&>(v[index]);
}
//! \}
public:
ASMJIT_INLINE_NODEBUG void init(const Vec& op0) noexcept {
OpArray::init(op0);
}
ASMJIT_INLINE_NODEBUG void init(const Vec& op0, const Vec& op1) noexcept {
OpArray::init(op0, op1);
}
ASMJIT_INLINE_NODEBUG void init(const Vec& op0, const Vec& op1, const Vec& op2) noexcept {
OpArray::init(op0, op1, op2);
}
ASMJIT_INLINE_NODEBUG void init(const Vec& op0, const Vec& op1, const Vec& op2, const Vec& op3) noexcept {
OpArray::init(op0, op1, op2, op3);
}
ASMJIT_INLINE_NODEBUG void init(const Vec& op0, const Vec& op1, const Vec& op2, const Vec& op3, const Vec& op4) noexcept {
OpArray::init(op0, op1, op2, op3, op4);
}
ASMJIT_INLINE_NODEBUG void init(const Vec& op0, const Vec& op1, const Vec& op2, const Vec& op3, const Vec& op4, const Vec& op5) noexcept {
OpArray::init(op0, op1, op2, op3, op4, op5);
}
ASMJIT_INLINE_NODEBUG void init(const Vec& op0, const Vec& op1, const Vec& op2, const Vec& op3, const Vec& op4, const Vec& op5, const Vec& op6) noexcept {
OpArray::init(op0, op1, op2, op3, op4, op5, op6);
}
ASMJIT_INLINE_NODEBUG void init(const Vec& op0, const Vec& op1, const Vec& op2, const Vec& op3, const Vec& op4, const Vec& op5, const Vec& op6, const Vec& op7) noexcept {
OpArray::init(op0, op1, op2, op3, op4, op5, op6, op7);
}
ASMJIT_INLINE_NODEBUG void init(const Vec* array, size_t size) noexcept {
OpArray::init(array, size);
}
ASMJIT_INLINE_NODEBUG void init(const VecArray& other) noexcept {
OpArray::init(other);
}
ASMJIT_INLINE Vec& at_unrestricted(size_t index) noexcept {
ASMJIT_ASSERT(index < kMaxSize);
return static_cast<Vec&>(v[index]);
}
ASMJIT_INLINE const Vec& at_unrestricted(size_t index) const noexcept {
ASMJIT_ASSERT(index < kMaxSize);
return static_cast<const Vec&>(v[index]);
}
ASMJIT_INLINE void reassign(size_t index, const Vec& new_vec) noexcept {
ASMJIT_ASSERT(index < kMaxSize);
v[index] = new_vec;
}
ASMJIT_INLINE_NODEBUG VecArray lo() const noexcept { return VecArray(*this, 0, 1, (_size + 1) / 2); }
ASMJIT_INLINE_NODEBUG VecArray hi() const noexcept { return VecArray(*this, _size > 1 ? (_size + 1) / 2 : 0, 1, _size); }
ASMJIT_INLINE_NODEBUG VecArray even() const noexcept { return VecArray(*this, 0, 2, _size); }
ASMJIT_INLINE_NODEBUG VecArray odd() const noexcept { return VecArray(*this, _size > 1, 2, _size); }
ASMJIT_INLINE_NODEBUG VecArray even_odd(size_t from) const noexcept { return VecArray(*this, _size > 1 ? from : 0, 2, _size); }
ASMJIT_INLINE_NODEBUG VecArray half() const noexcept { return VecArray(*this, 0, 1, (_size + 1) / 2); }
ASMJIT_INLINE_NODEBUG VecArray every_nth(size_t n) const noexcept { return VecArray(*this, 0, n, _size); }
ASMJIT_INLINE_NODEBUG void truncate(size_t new_size) noexcept {
_size = Support::min(_size, new_size);
}
ASMJIT_INLINE VecArray clone_as(OperandSignature signature) const noexcept {
VecArray out(*this);
for (size_t i = 0; i < out.size(); i++) {
out.v[i].set_signature(signature);
}
return out;
}
ASMJIT_INLINE_NODEBUG VecWidth vec_width() const noexcept { return VecWidthUtils::vec_width_of(v[0].as<Vec>()); }
ASMJIT_INLINE_NODEBUG void set_vec_width(VecWidth vw) noexcept {
OperandSignature signature = VecWidthUtils::signature_of(vw);
for (size_t i = 0; i < size(); i++) {
v[i].set_signature(signature);
}
}
ASMJIT_INLINE_NODEBUG VecArray clone_as(VecWidth vw) const noexcept { return clone_as(VecWidthUtils::signature_of(vw)); }
ASMJIT_INLINE_NODEBUG VecArray clone_as(const Reg& reg) const noexcept { return clone_as(reg.signature()); }
ASMJIT_INLINE_NODEBUG bool is_vec128() const noexcept { return v[0].is_vec128(); }
ASMJIT_INLINE_NODEBUG bool is_vec256() const noexcept { return v[0].is_vec256(); }
ASMJIT_INLINE_NODEBUG bool is_vec512() const noexcept { return v[0].is_vec512(); }
#if defined(ASMJIT_UJIT_X86)
ASMJIT_INLINE_NODEBUG VecArray xmm() const noexcept { return clone_as(OperandSignature{RegTraits<RegType::kVec128>::kSignature}); }
ASMJIT_INLINE_NODEBUG VecArray ymm() const noexcept { return clone_as(OperandSignature{RegTraits<RegType::kVec256>::kSignature}); }
ASMJIT_INLINE_NODEBUG VecArray zmm() const noexcept { return clone_as(OperandSignature{RegTraits<RegType::kVec512>::kSignature}); }
#endif
// Iterator compatibility.
ASMJIT_INLINE_NODEBUG Vec* begin() noexcept { return reinterpret_cast<Vec*>(v); }
ASMJIT_INLINE_NODEBUG Vec* end() noexcept { return reinterpret_cast<Vec*>(v) + _size; }
ASMJIT_INLINE_NODEBUG const Vec* begin() const noexcept { return reinterpret_cast<const Vec*>(v); }
ASMJIT_INLINE_NODEBUG const Vec* end() const noexcept { return reinterpret_cast<const Vec*>(v) + _size; }
ASMJIT_INLINE_NODEBUG const Vec* cbegin() const noexcept { return reinterpret_cast<const Vec*>(v); }
ASMJIT_INLINE_NODEBUG const Vec* cend() const noexcept { return reinterpret_cast<const Vec*>(v) + _size; }
};
//! JIT Utilities used by UniCompiler and other parts of the library.
namespace OpUtils {
template<typename T>
static ASMJIT_INLINE void reset_var_array(T* array, size_t size) noexcept {
for (size_t i = 0; i < size; i++)
array[i].reset();
}
template<typename T>
static ASMJIT_INLINE void reset_var_struct(T* data, size_t size = sizeof(T)) noexcept {
reset_var_array(reinterpret_cast<Reg*>(data), size / sizeof(Reg));
}
static ASMJIT_INLINE_NODEBUG const Operand_& first_op(const Operand_& operand) noexcept { return operand; }
static ASMJIT_INLINE_NODEBUG const Operand_& first_op(const OpArray& op_array) noexcept { return op_array[0]; }
static ASMJIT_INLINE_NODEBUG size_t count_op(const Operand_&) noexcept { return 1u; }
static ASMJIT_INLINE_NODEBUG size_t count_op(const OpArray& op_array) noexcept { return op_array.size(); }
} // {OpUtils}
// Swizzle & Permutations
// ----------------------
struct Swizzle2 {
uint32_t value;
ASMJIT_INLINE_CONSTEXPR bool operator==(const Swizzle2& other) const noexcept { return value == other.value; }
ASMJIT_INLINE_CONSTEXPR bool operator!=(const Swizzle2& other) const noexcept { return value != other.value; }
};
struct Swizzle4 {
uint32_t value;
ASMJIT_INLINE_CONSTEXPR bool operator==(const Swizzle4& other) const noexcept { return value == other.value; }
ASMJIT_INLINE_CONSTEXPR bool operator!=(const Swizzle4& other) const noexcept { return value != other.value; }
};
//! Constructs a backend-independent 2-element vector swizzle parameter.
static ASMJIT_INLINE_CONSTEXPR Swizzle2 swizzle(uint8_t b, uint8_t a) noexcept {
return Swizzle2{(uint32_t(b) << 8) | a};
}
//! Constructs a backend-independent 4-element vector swizzle parameter.
static ASMJIT_INLINE_CONSTEXPR Swizzle4 swizzle(uint8_t d, uint8_t c, uint8_t b, uint8_t a) noexcept {
return Swizzle4{(uint32_t(d) << 24) | (uint32_t(c) << 16) | (uint32_t(b) << 8) | a};
}
enum class Perm2x128 : uint32_t {
kALo = 0,
kAHi = 1,
kBLo = 2,
kBHi = 3,
kZero = 8
};
//! Constructs a backend-independent permutation of 128-bit lanes.
//!
//! \note This is currently only used by AVX2 and AVX-512 backends.
static ASMJIT_INLINE_CONSTEXPR uint8_t perm_2x128_imm(Perm2x128 hi, Perm2x128 lo) noexcept {
return uint8_t((uint32_t(hi) << 4) | (uint32_t(lo)));
}
// Injecting
// ---------
//! Provides scope-based code injection.
//!
//! Scope-based code injection is used in some places to inject code at a specific point in the generated code.
//! When a pipeline is initialized each part can remember certain place where it could inject code in the future
//! as at the initialization phase it still doesn't know whether everything required to generate the code in place.
class ScopedInjector {
ASMJIT_NONCOPYABLE(ScopedInjector)
public:
//! \name Members
//! \{
BaseCompiler* _cc {};
BaseNode** _hook {};
BaseNode* _prev {};
bool _hook_was_cursor = false;
//! \}
//! \name Construction & Destruction
//! \{
ASMJIT_INLINE ScopedInjector(BaseCompiler* cc, BaseNode** hook) noexcept
: _cc(cc),
_hook(hook),
_prev(cc->set_cursor(*hook)),
_hook_was_cursor(*hook == _prev) {}
ASMJIT_INLINE ~ScopedInjector() noexcept {
*_hook = _cc->cursor();
if (!_hook_was_cursor) {
_cc->set_cursor(_prev);
}
}
//! \}
};
//! \}
ASMJIT_END_SUB_NAMESPACE
#endif // !ASMJIT_NO_UJIT
#endif // ASMJIT_UJIT_JITBASE_P_H_INCLUDED