mirror of
https://github.com/libretro/parallel-ps2.git
synced 2026-11-11 03:11:30 -10:00
Three changes, one intent: bare `make` and bare cmake now build the C89 emitter with nothing to specify, and common/emitter contains only the six headers the recompilers actually use (c89emit.h, x86emitter.h, x86types.h, instructions.h, legacy_instructions.h, x86emitter_shim.h). Default flip. Makefile gains `C89_EMITTER ?= 1` -- set BEFORE the Makefile.common include, because the source exclusion there reads it. The first attempt set it next to the CXXFLAGS append at line 839, after the include: the define applied but the exclusion never fired, and the build went on linking reference objects it no longer needed. Caught by `make -n` grep: a bare plan now names zero reference objects, a C89_EMITTER=0 plan names all eight. CMake's option defaults to ON with the same semantics (-DC89_EMITTER=OFF for the reference). Reference headers. internal.h, legacy_internal.h and the thirteen-header implement/ tree move to tests/emitter/reference beside the sources that need them. internal.h now includes the implement set itself -- it is the reference implementation's own root and must be self-sufficient; x86types.h includes them only under the reference configuration, for the extern declarations in instructions.h's #else branches. The three G*Type operator enums move into x86types.h: recompilers name their values, which makes them API, not implementation. iR5900-32.cpp's MOVQ-workaround include of internal.h was already reference-gated and is repointed. cmake's installed- header list is repointed too (the stale entries failed configure). Legacy jump API. The fourteen rel8/rel32 wrappers become macros over two static helpers (J8Rel, J32Rel), matching the C89 core's idiom. The helpers are static -- internal linkage per TU -- so there is no header-linkage subtlety left to get wrong, where the previous inline definitions carried one. The speculative conditional mode initially drafted into J32Rel was removed before commit: only the unconditional form has callers. The legacy SSE C-API wrappers (SSE_MINSS_XMM_to_XMM and friends) now emit through E_SSE_RR directly instead of hopping through the C++ shim objects. Their constants are proven end-to-end in newbind: reference function vs C89 macro across all register pairs, 2,048 cases within the 110,070 total. Verified: six suites green, switchover identical, both compile gates 31/31 in both configurations, bare make links at 11,351,104 bytes with zero reference encoder symbols and boots, cmake configure passes in both configurations. The cmake three-target build has not completed in this container (cmake 4 needs -DCMAKE_POLICY_VERSION_MINIMUM=3.5 for the vendored cpuinfo; GL dev packages were also missing) -- it needs one run on a normally-provisioned machine. C89_EMITTER=0 static: the gates and suites compile and byte-verify that configuration; its last full link ran two commits ago unchanged.
710 lines
26 KiB
C++
710 lines
26 KiB
C++
/* PCSX2 - PS2 Emulator for PCs
|
|
* Copyright (C) 2002-2010 PCSX2 Dev Team
|
|
*
|
|
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
|
* of the GNU Lesser General Public License as published by the Free Software Found-
|
|
* ation, either version 3 of the License, or (at your option) any later version.
|
|
*
|
|
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with PCSX2.
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "tests/emitter/reference/internal.h"
|
|
#include "common/VectorIntrin.h"
|
|
|
|
#define xCVTDQ2PD(to, from) OpWriteSSE(0xf3, 0xe6)
|
|
|
|
namespace x86Emitter
|
|
{
|
|
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_DestRegEither xPAND = {0x66, 0xdb};
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_DestRegEither xPANDN = {0x66, 0xdf};
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_DestRegEither xPOR = {0x66, 0xeb};
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_DestRegEither xPXOR = {0x66, 0xef};
|
|
#endif
|
|
|
|
// [SSE-4.1] Performs a bitwise AND of dest against src, and sets the ZF flag
|
|
// only if all bits in the result are 0. PTEST also sets the CF flag according
|
|
// to the following condition: (xmm2/m128 AND NOT xmm1) == 0;
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_DestRegSSE xPTEST = {0x66, 0x1738};
|
|
|
|
#endif
|
|
// =====================================================================================================
|
|
// SSE Conversion Operations, as looney as they are.
|
|
// =====================================================================================================
|
|
// These enforce pointer strictness for Indirect forms, due to the otherwise completely confusing
|
|
// nature of the functions. (so if a function expects an m32, you must use (u32*) or ptr32[]).
|
|
//
|
|
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xCVTDQ2PS(const xRegisterSSE& to, const xRegisterSSE& from) { OpWriteSSE(0x00, 0x5b); }
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xCVTDQ2PS(const xRegisterSSE& to, const xIndirect128& from) { OpWriteSSE(0x00, 0x5b); }
|
|
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xCVTSD2SS(const xRegisterSSE& to, const xRegisterSSE& from) { OpWriteSSE(0xf2, 0x5a); }
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xCVTSD2SS(const xRegisterSSE& to, const xIndirect64& from) { OpWriteSSE(0xf2, 0x5a); }
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xCVTSI2SS(const xRegisterSSE& to, const xRegister32or64& from) { OpWriteSSE(0xf3, 0x2a); }
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xCVTSI2SS(const xRegisterSSE& to, const xIndirect32& from) { OpWriteSSE(0xf3, 0x2a); }
|
|
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xCVTSS2SD(const xRegisterSSE& to, const xRegisterSSE& from) { OpWriteSSE(0xf3, 0x5a); }
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xCVTSS2SD(const xRegisterSSE& to, const xIndirect32& from) { OpWriteSSE(0xf3, 0x5a); }
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xCVTSS2SI(const xRegister32or64& to, const xRegisterSSE& from) { OpWriteSSE(0xf3, 0x2d); }
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xCVTSS2SI(const xRegister32or64& to, const xIndirect32& from) { OpWriteSSE(0xf3, 0x2d); }
|
|
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xCVTTPS2DQ(const xRegisterSSE& to, const xRegisterSSE& from) { OpWriteSSE(0xf3, 0x5b); }
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xCVTTPS2DQ(const xRegisterSSE& to, const xIndirect128& from) { OpWriteSSE(0xf3, 0x5b); }
|
|
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xCVTTSD2SI(const xRegister32or64& to, const xRegisterSSE& from) { OpWriteSSE(0xf2, 0x2c); }
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xCVTTSD2SI(const xRegister32or64& to, const xIndirect64& from) { OpWriteSSE(0xf2, 0x2c); }
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xCVTTSS2SI(const xRegister32or64& to, const xRegisterSSE& from) { OpWriteSSE(0xf3, 0x2c); }
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xCVTTSS2SI(const xRegister32or64& to, const xIndirect32& from) { OpWriteSSE(0xf3, 0x2c); }
|
|
|
|
|
|
#endif
|
|
// ------------------------------------------------------------------------
|
|
|
|
void xImplSimd_DestRegSSE::operator()(const xRegisterSSE& to, const xRegisterSSE& from) const { OpWriteSSE(Prefix, Opcode); }
|
|
void xImplSimd_DestRegSSE::operator()(const xRegisterSSE& to, const xIndirectVoid& from) const { OpWriteSSE(Prefix, Opcode); }
|
|
|
|
void xImplSimd_DestRegImmSSE::operator()(const xRegisterSSE& to, const xRegisterSSE& from, u8 imm) const { xOpWrite0F(Prefix, Opcode, to, from, imm); }
|
|
void xImplSimd_DestRegImmSSE::operator()(const xRegisterSSE& to, const xIndirectVoid& from, u8 imm) const { xOpWrite0F(Prefix, Opcode, to, from, imm); }
|
|
|
|
void xImplSimd_DestRegEither::operator()(const xRegisterSSE& to, const xRegisterSSE& from) const { OpWriteSSE(Prefix, Opcode); }
|
|
void xImplSimd_DestRegEither::operator()(const xRegisterSSE& to, const xIndirectVoid& from) const { OpWriteSSE(Prefix, Opcode); }
|
|
|
|
void xImplSimd_DestSSE_CmpImm::operator()(const xRegisterSSE& to, const xRegisterSSE& from, SSE2_ComparisonType imm) const { xOpWrite0F(Prefix, Opcode, to, from, imm); }
|
|
void xImplSimd_DestSSE_CmpImm::operator()(const xRegisterSSE& to, const xIndirectVoid& from, SSE2_ComparisonType imm) const { xOpWrite0F(Prefix, Opcode, to, from, imm); }
|
|
|
|
// =====================================================================================================
|
|
// SIMD Arithmetic Instructions
|
|
// =====================================================================================================
|
|
|
|
void _SimdShiftHelper::operator()(const xRegisterSSE& to, const xRegisterSSE& from) const { OpWriteSSE(Prefix, Opcode); }
|
|
void _SimdShiftHelper::operator()(const xRegisterSSE& to, const xIndirectVoid& from) const { OpWriteSSE(Prefix, Opcode); }
|
|
|
|
|
|
void _SimdShiftHelper::operator()(const xRegisterSSE& to, u8 imm8) const
|
|
{
|
|
xOpWrite0F(0x66, OpcodeImm, (int)Modcode, to);
|
|
xWrite8(imm8);
|
|
}
|
|
|
|
void xImplSimd_Shift::DQ(const xRegisterSSE& to, u8 imm8) const
|
|
{
|
|
xOpWrite0F(0x66, 0x73, (int)Q.Modcode + 1, to, imm8);
|
|
}
|
|
|
|
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_ShiftWithoutQ xPSRA =
|
|
{
|
|
{0x66, 0xe1, 0x71, 4}, // W
|
|
{0x66, 0xe2, 0x72, 4} // D
|
|
};
|
|
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_Shift xPSRL =
|
|
{
|
|
{0x66, 0xd1, 0x71, 2}, // W
|
|
{0x66, 0xd2, 0x72, 2}, // D
|
|
{0x66, 0xd3, 0x73, 2}, // Q
|
|
};
|
|
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_Shift xPSLL =
|
|
{
|
|
{0x66, 0xf1, 0x71, 6}, // W
|
|
{0x66, 0xf2, 0x72, 6}, // D
|
|
{0x66, 0xf3, 0x73, 6}, // Q
|
|
};
|
|
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_AddSub xPADD =
|
|
{
|
|
{0x66, 0xdc + 0x20}, // B
|
|
{0x66, 0xdc + 0x21}, // W
|
|
{0x66, 0xdc + 0x22}, // D
|
|
{0x66, 0xd4}, // Q
|
|
|
|
{0x66, 0xdc + 0x10}, // SB
|
|
{0x66, 0xdc + 0x11}, // SW
|
|
{0x66, 0xdc}, // USB
|
|
{0x66, 0xdc + 1}, // USW
|
|
};
|
|
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_AddSub xPSUB =
|
|
{
|
|
{0x66, 0xd8 + 0x20}, // B
|
|
{0x66, 0xd8 + 0x21}, // W
|
|
{0x66, 0xd8 + 0x22}, // D
|
|
{0x66, 0xfb}, // Q
|
|
|
|
{0x66, 0xd8 + 0x10}, // SB
|
|
{0x66, 0xd8 + 0x11}, // SW
|
|
{0x66, 0xd8}, // USB
|
|
{0x66, 0xd8 + 1}, // USW
|
|
};
|
|
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_PMul xPMUL =
|
|
{
|
|
{0x66, 0xd5}, // LW
|
|
{0x66, 0xe5}, // HW
|
|
{0x66, 0xe4}, // HUW
|
|
{0x66, 0xf4}, // UDQ
|
|
|
|
{0x66, 0x0b38}, // HRSW
|
|
{0x66, 0x4038}, // LD
|
|
{0x66, 0x2838}, // DQ
|
|
};
|
|
#endif
|
|
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_Sqrt xSQRT =
|
|
{
|
|
{0x00, 0x51}, // PS
|
|
{0xf3, 0x51}, // SS
|
|
{0xf2, 0x51} // SD
|
|
};
|
|
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_PAbsolute xPABS =
|
|
{
|
|
{0x66, 0x1c38}, // B
|
|
{0x66, 0x1d38}, // W
|
|
{0x66, 0x1e38} // D
|
|
};
|
|
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_PMultAdd xPMADD =
|
|
{
|
|
{0x66, 0xf5}, // WD
|
|
{0x66, 0xf438}, // UBSW
|
|
};
|
|
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_DotProduct xDP =
|
|
{
|
|
{0x66, 0x403a}, // PS
|
|
{0x66, 0x413a}, // PD
|
|
};
|
|
|
|
#endif
|
|
// =====================================================================================================
|
|
// SIMD Comparison Instructions
|
|
// =====================================================================================================
|
|
|
|
#ifndef PCSX2_C89_EMITTER
|
|
void xImplSimd_Compare::PS(const xRegisterSSE& to, const xRegisterSSE& from) const { xOpWrite0F(0x00, 0xc2, to, from, (u8)CType); }
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
void xImplSimd_Compare::PS(const xRegisterSSE& to, const xIndirectVoid& from) const { xOpWrite0F(0x00, 0xc2, to, from, (u8)CType); }
|
|
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
void xImplSimd_Compare::PD(const xRegisterSSE& to, const xRegisterSSE& from) const { xOpWrite0F(0x66, 0xc2, to, from, (u8)CType); }
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
void xImplSimd_Compare::PD(const xRegisterSSE& to, const xIndirectVoid& from) const { xOpWrite0F(0x66, 0xc2, to, from, (u8)CType); }
|
|
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
void xImplSimd_Compare::SS(const xRegisterSSE& to, const xRegisterSSE& from) const { xOpWrite0F(0xf3, 0xc2, to, from, (u8)CType); }
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
void xImplSimd_Compare::SS(const xRegisterSSE& to, const xIndirectVoid& from) const { xOpWrite0F(0xf3, 0xc2, to, from, (u8)CType); }
|
|
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
void xImplSimd_Compare::SD(const xRegisterSSE& to, const xRegisterSSE& from) const { xOpWrite0F(0xf2, 0xc2, to, from, (u8)CType); }
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
void xImplSimd_Compare::SD(const xRegisterSSE& to, const xIndirectVoid& from) const { xOpWrite0F(0xf2, 0xc2, to, from, (u8)CType); }
|
|
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_MinMax xMIN =
|
|
{
|
|
{0x00, 0x5d}, // PS
|
|
{0x66, 0x5d}, // PD
|
|
{0xf3, 0x5d}, // SS
|
|
{0xf2, 0x5d}, // SD
|
|
};
|
|
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_MinMax xMAX =
|
|
{
|
|
{0x00, 0x5f}, // PS
|
|
{0x66, 0x5f}, // PD
|
|
{0xf3, 0x5f}, // SS
|
|
{0xf2, 0x5f}, // SD
|
|
};
|
|
|
|
#endif
|
|
// [TODO] : Merge this into the xCMP class, so that they are notation as: xCMP.EQ
|
|
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_Compare xCMPEQ = {SSE2_Equal};
|
|
const xImplSimd_Compare xCMPLT = {SSE2_Less};
|
|
const xImplSimd_Compare xCMPNLT = {SSE2_NotLess};
|
|
const xImplSimd_Compare xCMPNLE = {SSE2_NotLessOrEqual};
|
|
const xImplSimd_Compare xCMPUNORD = {SSE2_Unordered};
|
|
#endif
|
|
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_COMI xUCOMI =
|
|
{
|
|
{0x00, 0x2e}, // SS
|
|
{0x66, 0x2e}, // SD
|
|
};
|
|
#endif
|
|
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_PCompare xPCMP =
|
|
{
|
|
{0x66, 0x74}, // EQB
|
|
{0x66, 0x75}, // EQW
|
|
{0x66, 0x76}, // EQD
|
|
|
|
{0x66, 0x64}, // GTB
|
|
{0x66, 0x65}, // GTW
|
|
{0x66, 0x66}, // GTD
|
|
};
|
|
#endif
|
|
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_PMinMax xPMIN =
|
|
{
|
|
{0x66, 0xda}, // UB
|
|
{0x66, 0xea}, // SW
|
|
{0x66, 0x3838}, // SB
|
|
{0x66, 0x3938}, // SD
|
|
|
|
{0x66, 0x3a38}, // UW
|
|
{0x66, 0x3b38}, // UD
|
|
};
|
|
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_PMinMax xPMAX =
|
|
{
|
|
{0x66, 0xde}, // UB
|
|
{0x66, 0xee}, // SW
|
|
{0x66, 0x3c38}, // SB
|
|
{0x66, 0x3d38}, // SD
|
|
|
|
{0x66, 0x3e38}, // UW
|
|
{0x66, 0x3f38}, // UD
|
|
};
|
|
|
|
#endif
|
|
// =====================================================================================================
|
|
// SIMD Shuffle/Pack (Shuffle puck?)
|
|
// =====================================================================================================
|
|
|
|
void xImplSimd_Shuffle::PS(const xRegisterSSE& to, const xRegisterSSE& from, u8 selector) const
|
|
{
|
|
xOpWrite0F(0, 0xc6, to, from, selector);
|
|
}
|
|
|
|
void xImplSimd_Shuffle::PS(const xRegisterSSE& to, const xIndirectVoid& from, u8 selector) const
|
|
{
|
|
xOpWrite0F(0, 0xc6, to, from, selector);
|
|
}
|
|
|
|
void xImplSimd_Shuffle::PD(const xRegisterSSE& to, const xRegisterSSE& from, u8 selector) const
|
|
{
|
|
xOpWrite0F(0x66, 0xc6, to, from, selector & 0x3);
|
|
}
|
|
|
|
void xImplSimd_Shuffle::PD(const xRegisterSSE& to, const xIndirectVoid& from, u8 selector) const
|
|
{
|
|
xOpWrite0F(0x66, 0xc6, to, from, selector & 0x3);
|
|
}
|
|
|
|
void xImplSimd_PInsert::B(const xRegisterSSE& to, const xRegister32& from, u8 imm8) const { xOpWrite0F(0x66, 0x203a, to, from, imm8); }
|
|
void xImplSimd_PInsert::B(const xRegisterSSE& to, const xIndirect32& from, u8 imm8) const { xOpWrite0F(0x66, 0x203a, to, from, imm8); }
|
|
|
|
void xImplSimd_PInsert::W(const xRegisterSSE& to, const xRegister32& from, u8 imm8) const { xOpWrite0F(0x66, 0xc4, to, from, imm8); }
|
|
void xImplSimd_PInsert::W(const xRegisterSSE& to, const xIndirect32& from, u8 imm8) const { xOpWrite0F(0x66, 0xc4, to, from, imm8); }
|
|
|
|
void xImplSimd_PInsert::D(const xRegisterSSE& to, const xRegister32& from, u8 imm8) const { xOpWrite0F(0x66, 0x223a, to, from, imm8); }
|
|
void xImplSimd_PInsert::D(const xRegisterSSE& to, const xIndirect32& from, u8 imm8) const { xOpWrite0F(0x66, 0x223a, to, from, imm8); }
|
|
|
|
void xImplSimd_PInsert::Q(const xRegisterSSE& to, const xRegister64& from, u8 imm8) const { xOpWrite0F(0x66, 0x223a, to, from, imm8); }
|
|
void xImplSimd_PInsert::Q(const xRegisterSSE& to, const xIndirect64& from, u8 imm8) const { xOpWrite0F(0x66, 0x223a, to, from, imm8); }
|
|
|
|
void SimdImpl_PExtract::B(const xRegister32& to, const xRegisterSSE& from, u8 imm8) const { xOpWrite0F(0x66, 0x143a, from, to, imm8); }
|
|
void SimdImpl_PExtract::B(const xIndirect32& dest, const xRegisterSSE& from, u8 imm8) const { xOpWrite0F(0x66, 0x143a, from, dest, imm8); }
|
|
|
|
void SimdImpl_PExtract::W(const xRegister32& to, const xRegisterSSE& from, u8 imm8) const { xOpWrite0F(0x66, 0xc5, from, to, imm8); }
|
|
void SimdImpl_PExtract::W(const xIndirect32& dest, const xRegisterSSE& from, u8 imm8) const { xOpWrite0F(0x66, 0x153a, from, dest, imm8); }
|
|
|
|
void SimdImpl_PExtract::D(const xRegister32& to, const xRegisterSSE& from, u8 imm8) const { xOpWrite0F(0x66, 0x163a, from, to, imm8); }
|
|
void SimdImpl_PExtract::D(const xIndirect32& dest, const xRegisterSSE& from, u8 imm8) const { xOpWrite0F(0x66, 0x163a, from, dest, imm8); }
|
|
|
|
void SimdImpl_PExtract::Q(const xRegister64& to, const xRegisterSSE& from, u8 imm8) const { xOpWrite0F(0x66, 0x163a, from, to, imm8); }
|
|
void SimdImpl_PExtract::Q(const xIndirect64& dest, const xRegisterSSE& from, u8 imm8) const { xOpWrite0F(0x66, 0x163a, from, dest, imm8); }
|
|
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_Shuffle xSHUF = {};
|
|
#endif
|
|
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_PShuffle xPSHUF =
|
|
{
|
|
{0x66, 0x70}, // D
|
|
{0xf2, 0x70}, // LW
|
|
{0xf3, 0x70}, // HW
|
|
|
|
{0x66, 0x0038}, // B
|
|
};
|
|
#endif
|
|
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const SimdImpl_PUnpack xPUNPCK =
|
|
{
|
|
{0x66, 0x60}, // LBW
|
|
{0x66, 0x61}, // LWD
|
|
{0x66, 0x62}, // LDQ
|
|
{0x66, 0x6c}, // LQDQ
|
|
|
|
{0x66, 0x68}, // HBW
|
|
{0x66, 0x69}, // HWD
|
|
{0x66, 0x6a}, // HDQ
|
|
{0x66, 0x6d}, // HQDQ
|
|
};
|
|
#endif
|
|
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const SimdImpl_Pack xPACK =
|
|
{
|
|
{0x66, 0x63}, // SSWB
|
|
{0x66, 0x6b}, // SSDW
|
|
{0x66, 0x67}, // USWB
|
|
{0x66, 0x2b38}, // USDW
|
|
};
|
|
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_Unpack xUNPCK =
|
|
{
|
|
{0x00, 0x15}, // HPS
|
|
{0x66, 0x15}, // HPD
|
|
{0x00, 0x14}, // LPS
|
|
{0x66, 0x14}, // LPD
|
|
};
|
|
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_PInsert xPINSR;
|
|
const SimdImpl_PExtract xPEXTR;
|
|
#endif
|
|
|
|
// =====================================================================================================
|
|
// SIMD Move And Blend Instructions
|
|
// =====================================================================================================
|
|
|
|
void xImplSimd_MovHL::PS(const xRegisterSSE& to, const xIndirectVoid& from) const { xOpWrite0F(0, Opcode, to, from); }
|
|
void xImplSimd_MovHL::PS(const xIndirectVoid& to, const xRegisterSSE& from) const { xOpWrite0F(0, Opcode + 1, from, to); }
|
|
|
|
void xImplSimd_MovHL::PD(const xRegisterSSE& to, const xIndirectVoid& from) const { xOpWrite0F(0x66, Opcode, to, from); }
|
|
void xImplSimd_MovHL::PD(const xIndirectVoid& to, const xRegisterSSE& from) const { xOpWrite0F(0x66, Opcode + 1, from, to); }
|
|
|
|
void xImplSimd_MovHL_RtoR::PS(const xRegisterSSE& to, const xRegisterSSE& from) const { xOpWrite0F(0, Opcode, to, from); }
|
|
void xImplSimd_MovHL_RtoR::PD(const xRegisterSSE& to, const xRegisterSSE& from) const { xOpWrite0F(0x66, Opcode, to, from); }
|
|
|
|
static const u16 MovPS_OpAligned = 0x28; // Aligned [aps] form
|
|
static const u16 MovPS_OpUnaligned = 0x10; // unaligned [ups] form
|
|
|
|
void xImplSimd_MoveSSE::operator()(const xRegisterSSE& to, const xRegisterSSE& from) const
|
|
{
|
|
if (to != from)
|
|
xOpWrite0F(Prefix, MovPS_OpAligned, to, from);
|
|
}
|
|
|
|
void xImplSimd_MoveSSE::operator()(const xRegisterSSE& to, const xIndirectVoid& from) const
|
|
{
|
|
// ModSib form is aligned if it's displacement-only and the displacement is aligned:
|
|
bool isReallyAligned = isAligned || (((from.Displacement & 0x0f) == 0) && from.Index.IsEmpty() && from.Base.IsEmpty());
|
|
|
|
xOpWrite0F(Prefix, isReallyAligned ? MovPS_OpAligned : MovPS_OpUnaligned, to, from);
|
|
}
|
|
|
|
void xImplSimd_MoveSSE::operator()(const xIndirectVoid& to, const xRegisterSSE& from) const
|
|
{
|
|
// ModSib form is aligned if it's displacement-only and the displacement is aligned:
|
|
bool isReallyAligned = isAligned || ((to.Displacement & 0x0f) == 0 && to.Index.IsEmpty() && to.Base.IsEmpty());
|
|
xOpWrite0F(Prefix, isReallyAligned ? MovPS_OpAligned + 1 : MovPS_OpUnaligned + 1, from, to);
|
|
}
|
|
|
|
static const u8 MovDQ_PrefixAligned = 0x66; // Aligned [dqa] form
|
|
static const u8 MovDQ_PrefixUnaligned = 0xf3; // unaligned [dqu] form
|
|
|
|
void xImplSimd_MoveDQ::operator()(const xRegisterSSE& to, const xRegisterSSE& from) const
|
|
{
|
|
if (to != from)
|
|
xOpWrite0F(MovDQ_PrefixAligned, 0x6f, to, from);
|
|
}
|
|
|
|
void xImplSimd_MoveDQ::operator()(const xRegisterSSE& to, const xIndirectVoid& from) const
|
|
{
|
|
// ModSib form is aligned if it's displacement-only and the displacement is aligned:
|
|
bool isReallyAligned = isAligned || ((from.Displacement & 0x0f) == 0 && from.Index.IsEmpty() && from.Base.IsEmpty());
|
|
xOpWrite0F(isReallyAligned ? MovDQ_PrefixAligned : MovDQ_PrefixUnaligned, 0x6f, to, from);
|
|
}
|
|
|
|
void xImplSimd_MoveDQ::operator()(const xIndirectVoid& to, const xRegisterSSE& from) const
|
|
{
|
|
// ModSib form is aligned if it's displacement-only and the displacement is aligned:
|
|
bool isReallyAligned = isAligned || ((to.Displacement & 0x0f) == 0 && to.Index.IsEmpty() && to.Base.IsEmpty());
|
|
|
|
// use opcode 0x7f : alternate ModRM encoding (reverse src/dst)
|
|
xOpWrite0F(isReallyAligned ? MovDQ_PrefixAligned : MovDQ_PrefixUnaligned, 0x7f, from, to);
|
|
}
|
|
|
|
void xImplSimd_PMove::BW(const xRegisterSSE& to, const xRegisterSSE& from) const { OpWriteSSE(0x66, OpcodeBase); }
|
|
void xImplSimd_PMove::BW(const xRegisterSSE& to, const xIndirect64& from) const { OpWriteSSE(0x66, OpcodeBase); }
|
|
|
|
void xImplSimd_PMove::BD(const xRegisterSSE& to, const xRegisterSSE& from) const { OpWriteSSE(0x66, OpcodeBase + 0x100); }
|
|
void xImplSimd_PMove::BD(const xRegisterSSE& to, const xIndirect32& from) const { OpWriteSSE(0x66, OpcodeBase + 0x100); }
|
|
|
|
void xImplSimd_PMove::BQ(const xRegisterSSE& to, const xRegisterSSE& from) const { OpWriteSSE(0x66, OpcodeBase + 0x200); }
|
|
void xImplSimd_PMove::BQ(const xRegisterSSE& to, const xIndirect16& from) const { OpWriteSSE(0x66, OpcodeBase + 0x200); }
|
|
|
|
void xImplSimd_PMove::WD(const xRegisterSSE& to, const xRegisterSSE& from) const { OpWriteSSE(0x66, OpcodeBase + 0x300); }
|
|
void xImplSimd_PMove::WD(const xRegisterSSE& to, const xIndirect64& from) const { OpWriteSSE(0x66, OpcodeBase + 0x300); }
|
|
|
|
void xImplSimd_PMove::WQ(const xRegisterSSE& to, const xRegisterSSE& from) const { OpWriteSSE(0x66, OpcodeBase + 0x400); }
|
|
void xImplSimd_PMove::WQ(const xRegisterSSE& to, const xIndirect32& from) const { OpWriteSSE(0x66, OpcodeBase + 0x400); }
|
|
|
|
void xImplSimd_PMove::DQ(const xRegisterSSE& to, const xRegisterSSE& from) const { OpWriteSSE(0x66, OpcodeBase + 0x500); }
|
|
void xImplSimd_PMove::DQ(const xRegisterSSE& to, const xIndirect64& from) const { OpWriteSSE(0x66, OpcodeBase + 0x500); }
|
|
|
|
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_MoveSSE xMOVAPS = {0x00, true};
|
|
const xImplSimd_MoveSSE xMOVUPS = {0x00, false};
|
|
#endif
|
|
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_MoveDQ xMOVDQA = {0x66, true};
|
|
const xImplSimd_MoveDQ xMOVDQU = {0xf3, false};
|
|
#endif
|
|
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_MovHL xMOVH = {0x16};
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_MovHL xMOVL = {0x12};
|
|
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_MovHL_RtoR xMOVLH = {0x16};
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_MovHL_RtoR xMOVHL = {0x12};
|
|
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_PBlend xPBLEND =
|
|
{
|
|
{0x66, 0x0e3a}, // W
|
|
{0x66, 0x1038}, // VB
|
|
};
|
|
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_Blend xBLEND =
|
|
{
|
|
{0x66, 0x0c3a}, // PS
|
|
{0x66, 0x0d3a}, // PD
|
|
{0x66, 0x1438}, // VPS
|
|
{0x66, 0x1538}, // VPD
|
|
};
|
|
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_PMove xPMOVSX = {0x2038};
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImplSimd_PMove xPMOVZX = {0x3038};
|
|
#endif
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
// MMX Mov Instructions (MOVD, MOVQ, MOVSS).
|
|
//
|
|
// Notes:
|
|
// * Some of the functions have been renamed to more clearly reflect what they actually
|
|
// do. Namely we've affixed "ZX" to several MOVs that take a register as a destination
|
|
// since that's what they do (MOVD clears upper 32/96 bits, etc).
|
|
//
|
|
// * MOVD has valid forms for MMX and XMM registers.
|
|
//
|
|
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xMOVDZX(const xRegisterSSE& to, const xRegister32or64& from) { xOpWrite0F(0x66, 0x6e, to, from); }
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xMOVDZX(const xRegisterSSE& to, const xIndirectVoid& src) { xOpWrite0F(0x66, 0x6e, to, src); }
|
|
#endif
|
|
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xMOVD(const xRegister32or64& to, const xRegisterSSE& from) { xOpWrite0F(0x66, 0x7e, from, to); }
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xMOVD(const xIndirectVoid& dest, const xRegisterSSE& from) { xOpWrite0F(0x66, 0x7e, from, dest); }
|
|
#endif
|
|
|
|
// Moves from XMM to XMM, with the *upper 64 bits* of the destination register
|
|
// being cleared to zero.
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xMOVQZX(const xRegisterSSE& to, const xRegisterSSE& from) { xOpWrite0F(0xf3, 0x7e, to, from); }
|
|
#endif
|
|
|
|
// Moves from XMM to XMM, with the *upper 64 bits* of the destination register
|
|
// being cleared to zero.
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xMOVQZX(const xRegisterSSE& to, const xIndirectVoid& src) { xOpWrite0F(0xf3, 0x7e, to, src); }
|
|
#endif
|
|
|
|
// Moves from XMM to XMM, with the *upper 64 bits* of the destination register
|
|
// being cleared to zero.
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xMOVQZX(const xRegisterSSE& to, const void* src) { xOpWrite0F(0xf3, 0x7e, to, src); }
|
|
#endif
|
|
|
|
// Moves lower quad of XMM to ptr64 (no bits are cleared)
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xMOVQ(const xIndirectVoid& dest, const xRegisterSSE& from) { xOpWrite0F(0x66, 0xd6, from, dest); }
|
|
#endif
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
#define IMPLEMENT_xMOVS(ssd, prefix) \
|
|
__fi void xMOV##ssd(const xRegisterSSE& to, const xRegisterSSE& from) \
|
|
{ \
|
|
if (to != from) \
|
|
xOpWrite0F(prefix, 0x10, to, from); \
|
|
} \
|
|
__fi void xMOV##ssd##ZX(const xRegisterSSE& to, const xIndirectVoid& from) { xOpWrite0F(prefix, 0x10, to, from); } \
|
|
__fi void xMOV##ssd(const xIndirectVoid& to, const xRegisterSSE& from) { xOpWrite0F(prefix, 0x11, from, to); }
|
|
|
|
#ifndef PCSX2_C89_EMITTER
|
|
IMPLEMENT_xMOVS(SS, 0xf3)
|
|
IMPLEMENT_xMOVS(SD, 0xf2)
|
|
#endif
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
// Non-temporal movs only support a register as a target (ie, load form only, no stores)
|
|
//
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xMOVMSKPS(const xRegister32& to, const xRegisterSSE& from) { xOpWrite0F(0, 0x50, to, from); }
|
|
#endif
|
|
__fi void xMOVMSKPD(const xRegister32& to, const xRegisterSSE& from) { xOpWrite0F(0x66, 0x50, to, from, true); }
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// INSERTPS / EXTRACTPS [SSE4.1 only!]
|
|
// --------------------------------------------------------------------------------------
|
|
// [TODO] these might be served better as classes, especially if other instructions use
|
|
// the M32,sse,imm form (I forget offhand if any do).
|
|
|
|
// [SSE-4.1] Insert a single-precision floating-point value from src into a specified
|
|
// location in dest, and selectively zero out the data elements in dest according to
|
|
// the mask field in the immediate byte. The source operand can be a memory location
|
|
// (32 bits) or an XMM register (lower 32 bits used).
|
|
//
|
|
// Imm8 provides three fields:
|
|
// * COUNT_S: The value of Imm8[7:6] selects the dword element from src. It is 0 if
|
|
// the source is a memory operand.
|
|
// * COUNT_D: The value of Imm8[5:4] selects the target dword element in dest.
|
|
// * ZMASK: Each bit of Imm8[3:0] selects a dword element in dest to be written
|
|
// with 0.0 if set to 1.
|
|
//
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xINSERTPS(const xRegisterSSE& to, const xRegisterSSE& from, u8 imm8) { xOpWrite0F(0x66, 0x213a, to, from, imm8); }
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xINSERTPS(const xRegisterSSE& to, const xIndirect32& from, u8 imm8) { xOpWrite0F(0x66, 0x213a, to, from, imm8); }
|
|
|
|
#endif
|
|
// [SSE-4.1] Extract a single-precision floating-point value from src at an offset
|
|
// determined by imm8[1-0]*32. The extracted single precision floating-point value
|
|
// is stored into the low 32-bits of dest (or at a 32-bit memory pointer).
|
|
//
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xEXTRACTPS(const xRegister32or64& to, const xRegisterSSE& from, u8 imm8) { xOpWrite0F(0x66, 0x173a, to, from, imm8); }
|
|
#endif
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xEXTRACTPS(const xIndirect32& dest, const xRegisterSSE& from, u8 imm8) { xOpWrite0F(0x66, 0x173a, from, dest, imm8); }
|
|
|
|
|
|
#endif
|
|
// =====================================================================================================
|
|
// Ungrouped Instructions!
|
|
// =====================================================================================================
|
|
|
|
// Load Streaming SIMD Extension Control/Status from Mem32.
|
|
#ifndef PCSX2_C89_EMITTER
|
|
__fi void xLDMXCSR(const xIndirect32& src)
|
|
{
|
|
xOpWrite0F(0, 0xae, 2, src);
|
|
}
|
|
#endif
|
|
} // namespace x86Emitter
|