mirror of
https://github.com/asmjit/asmjit.git
synced 2025-12-16 20:17:05 +03:00
* 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
735 lines
24 KiB
C++
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
|