pcsx2/common/emitter/implement/simd_helpers.h
2025-08-24 20:16:24 -05:00

161 lines
7.5 KiB
C++

// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
namespace x86Emitter
{
// =====================================================================================================
// xImpl_SIMD Types (template free!)
// =====================================================================================================
struct SIMDInstructionInfo {
/// The prefix byte of a simd instruction. These match up with their (E)VEX encodings.
enum class Prefix : u32 {
None = 0,
P66 = 1,
PF3 = 2,
PF2 = 3,
};
/// The opcode map of a simd instruction. These match up with their (E)VEX encodings.
enum class Map : u32 {
M0F = 1,
M0F38 = 2,
M0F3A = 3,
};
/// Whether an operation operates on float (ss, ps), integer (b, w, d, q), or double (sd, pd) data.
/// May be used to choose an appropriate mov instruction if one is needed.
enum class Type : u32 {
Float, Integer, Double
};
/// The main opcode
u32 opcode : 8;
/// Prefix byte
Prefix prefix : 2;
/// Opcode map
Map map : 5;
/// Information about the data this operation operates on. Ignored for instructions where the SSE4 and AVX versions have the same number of arguments.
Type type : 2;
/// For instructions like pslld, the data that should go into the reg field in place of the first src
u32 ext : 3;
/// If true, the two inputs to the function can be swapped without changing its result.
u32 is_commutative : 1;
/// If true, the dst and src1 must be the same in AVX (e.g. mov instructions, pshufd)
u32 is_mov : 1;
/// If true, get `W` from dst register instead of `w_bit`
u32 dst_w : 1;
/// If true, get `W` from src register instead of `w_bit`
u32 src_w : 1;
/// If true, the instruction has the VEX W bit set
u32 w_bit : 1;
constexpr SIMDInstructionInfo(u8 opcode_, u8 ext_ = 0)
: opcode(opcode_), prefix(Prefix::None), map(Map::M0F), type(Type::Float), ext(ext_)
, is_commutative(false), is_mov(false), dst_w(false), src_w(false), w_bit(false)
{
}
// For configuration using in a builder-style
constexpr SIMDInstructionInfo p66() const { SIMDInstructionInfo copy = *this; copy.prefix = Prefix::P66; return copy; }
constexpr SIMDInstructionInfo pf3() const { SIMDInstructionInfo copy = *this; copy.prefix = Prefix::PF3; return copy; }
constexpr SIMDInstructionInfo pf2() const { SIMDInstructionInfo copy = *this; copy.prefix = Prefix::PF2; return copy; }
constexpr SIMDInstructionInfo m0f38() const { SIMDInstructionInfo copy = *this; copy.map = Map::M0F38; return copy; }
constexpr SIMDInstructionInfo m0f3a() const { SIMDInstructionInfo copy = *this; copy.map = Map::M0F3A; return copy; }
constexpr SIMDInstructionInfo f() const { SIMDInstructionInfo copy = *this; copy.type = Type::Float; return copy; }
constexpr SIMDInstructionInfo i() const { SIMDInstructionInfo copy = *this; copy.type = Type::Integer; return copy; }
constexpr SIMDInstructionInfo d() const { SIMDInstructionInfo copy = *this; copy.type = Type::Double; return copy; }
constexpr SIMDInstructionInfo w() const { SIMDInstructionInfo copy = *this; copy.w_bit = true; return copy; }
constexpr SIMDInstructionInfo dstw() const { SIMDInstructionInfo copy = *this; copy.dst_w = true; return copy; }
constexpr SIMDInstructionInfo srcw() const { SIMDInstructionInfo copy = *this; copy.src_w = true; return copy; }
constexpr SIMDInstructionInfo commutative() const { SIMDInstructionInfo copy = *this; copy.is_commutative = true; return copy; }
constexpr SIMDInstructionInfo mov() const { SIMDInstructionInfo copy = *this; copy.is_mov = true; return copy; }
};
// ------------------------------------------------------------------------
// For implementing SSE/AVX logic operations that have two arguments in both SSE and AVX
// like MOVAPS, CVTPS2DQ, etc
//
struct xImplSimd_2Arg
{
SIMDInstructionInfo info;
constexpr xImplSimd_2Arg(SIMDInstructionInfo info_): info(info_.mov()) {}
void operator()(const xRegisterSSE& dst, const xRegisterSSE& src) const;
void operator()(const xRegisterSSE& dst, const xIndirectVoid& src) const;
};
// ------------------------------------------------------------------------
// For implementing SSE/AVX logic operations that have two arguments in both SSE and AVX, plus an immediate
// like PSHUFD
//
struct xImplSimd_2ArgImm
{
SIMDInstructionInfo info;
constexpr xImplSimd_2ArgImm(SIMDInstructionInfo info_): info(info_.mov()) {}
void operator()(const xRegisterSSE& dst, const xRegisterSSE& src, u8 imm) const;
void operator()(const xRegisterSSE& dst, const xIndirectVoid& src, u8 imm) const;
};
// ------------------------------------------------------------------------
// For implementing SSE/AVX logic operations that have three arguments AVX and two in SSE
// like ANDPS, ANDPD, etc
//
struct xImplSimd_3Arg
{
SIMDInstructionInfo info;
void operator()(const xRegisterSSE& dst, const xRegisterSSE& src) const { (*this)(dst, dst, src); }
void operator()(const xRegisterSSE& dst, const xIndirectVoid& src) const { (*this)(dst, dst, src); }
void operator()(const xRegisterSSE& dst, const xRegisterSSE& src1, const xRegisterSSE& src2) const;
void operator()(const xRegisterSSE& dst, const xRegisterSSE& src1, const xIndirectVoid& src2) const;
};
// ------------------------------------------------------------------------
// For implementing SSE/AVX logic operations that have three arguments AVX and two in SSE
// like SHUFPS, INSERTPS, etc
//
struct xImplSimd_3ArgImm
{
SIMDInstructionInfo info;
void operator()(const xRegisterSSE& dst, const xRegisterSSE& src, u8 imm) const { (*this)(dst, dst, src, imm); }
void operator()(const xRegisterSSE& dst, const xIndirectVoid& src, u8 imm) const { (*this)(dst, dst, src, imm); }
void operator()(const xRegisterSSE& dst, const xRegisterSSE& src1, const xRegisterSSE& src2, u8 imm) const;
void operator()(const xRegisterSSE& dst, const xRegisterSSE& src1, const xIndirectVoid& src2, u8 imm) const;
};
// ------------------------------------------------------------------------
// For implementing SSE/AVX logic operations that have three arguments AVX and two in SSE
// like SHUFPS, INSERTPS, etc
//
struct xImplSimd_3ArgCmp
{
SIMDInstructionInfo info;
void operator()(const xRegisterSSE& dst, const xRegisterSSE& src, SSE2_ComparisonType imm) const { (*this)(dst, dst, src, imm); }
void operator()(const xRegisterSSE& dst, const xIndirectVoid& src, SSE2_ComparisonType imm) const { (*this)(dst, dst, src, imm); }
void operator()(const xRegisterSSE& dst, const xRegisterSSE& src1, const xRegisterSSE& src2, SSE2_ComparisonType imm) const;
void operator()(const xRegisterSSE& dst, const xRegisterSSE& src1, const xIndirectVoid& src2, SSE2_ComparisonType imm) const;
};
// ------------------------------------------------------------------------
// For implementing SSE/AVX logic operations that have four arguments AVX and two in SSE (with an implicit xmm0)
// like PBLENDVB, BLENDVPS, etc
//
struct xImplSimd_4ArgBlend
{
SIMDInstructionInfo sse;
SIMDInstructionInfo avx;
void operator()(const xRegisterSSE& dst, const xRegisterSSE& src) const { (*this)(dst, dst, src, xmm0); }
void operator()(const xRegisterSSE& dst, const xIndirectVoid& src) const { (*this)(dst, dst, src, xmm0); }
void operator()(const xRegisterSSE& dst, const xRegisterSSE& src1, const xRegisterSSE& src2, const xRegisterSSE& src3) const;
void operator()(const xRegisterSSE& dst, const xRegisterSSE& src1, const xIndirectVoid& src2, const xRegisterSSE& src3) const;
};
} // end namespace x86Emitter