Add separate VMX/fpu mxcsr

Add support for constant operands for most fpu instructions
Remove constant folding for most fpu cpde
half float
This commit is contained in:
chss95cs@gmail.com
2022-07-31 08:56:36 -07:00
parent 3185b0ac9c
commit 968f656d96
18 changed files with 687 additions and 611 deletions

View File

@@ -692,6 +692,12 @@ void X64Backend::InitializeBackendContext(void* ctx) {
X64BackendContext* bctx = reinterpret_cast<X64BackendContext*>(
reinterpret_cast<intptr_t>(ctx) - sizeof(X64BackendContext));
bctx->ResolveFunction_Ptr = reinterpret_cast<void*>(&ResolveFunction);
bctx->mxcsr_fpu =
DEFAULT_FPU_MXCSR; // idk if this is right, check on rgh what the
// rounding on ppc is at startup
bctx->mxcsr_vmx = DEFAULT_VMX_MXCSR;
bctx->flags = 0;
// https://media.discordapp.net/attachments/440280035056943104/1000765256643125308/unknown.png
bctx->Ox1000 = 0x1000;
}
} // namespace x64

View File

@@ -37,9 +37,17 @@ typedef void (*ResolveFunctionThunk)();
// negatively index the membase reg)
struct X64BackendContext {
void* ResolveFunction_Ptr; // cached pointer to resolvefunction
unsigned int mxcsr_fpu; //currently, the way we implement rounding mode affects both vmx and the fpu
unsigned int mxcsr_vmx;
unsigned int flags; //bit 0 = 0 if mxcsr is fpu, else it is vmx
unsigned int Ox1000; // constant 0x1000 so we can shrink each tail emitted
// add of it by... 2 bytes lol
};
constexpr unsigned int DEFAULT_VMX_MXCSR =
0x8000 | // flush to zero
0x0040 | (_MM_MASK_MASK); // default rounding mode for vmx
constexpr unsigned int DEFAULT_FPU_MXCSR = 0x1F80;
class X64Backend : public Backend {
public:

View File

@@ -320,6 +320,8 @@ bool X64Emitter::Emit(HIRBuilder* builder, EmitFunctionInfo& func_info) {
// Body.
auto block = builder->first_block();
while (block) {
ForgetMxcsrMode(); // at start of block, mxcsr mode is undefined
// Mark block labels.
auto label = block->label_head;
while (label) {
@@ -490,6 +492,7 @@ uint64_t ResolveFunction(void* raw_context, uint64_t target_address) {
void X64Emitter::Call(const hir::Instr* instr, GuestFunction* function) {
assert_not_null(function);
ForgetMxcsrMode();
auto fn = static_cast<X64Function*>(function);
// Resolve address to the function to call and store in rax.
@@ -564,6 +567,7 @@ void X64Emitter::Call(const hir::Instr* instr, GuestFunction* function) {
void X64Emitter::CallIndirect(const hir::Instr* instr,
const Xbyak::Reg64& reg) {
ForgetMxcsrMode();
// Check if return.
if (instr->flags & hir::CALL_POSSIBLE_RETURN) {
cmp(reg.cvt32(), dword[rsp + StackLayout::GUEST_RET_ADDR]);
@@ -617,6 +621,7 @@ uint64_t UndefinedCallExtern(void* raw_context, uint64_t function_ptr) {
return 0;
}
void X64Emitter::CallExtern(const hir::Instr* instr, const Function* function) {
ForgetMxcsrMode();
bool undefined = true;
if (function->behavior() == Function::Behavior::kBuiltin) {
auto builtin_function = static_cast<const BuiltinFunction*>(function);
@@ -696,11 +701,13 @@ Xbyak::Reg64 X64Emitter::GetNativeParam(uint32_t param) {
}
// Important: If you change these, you must update the thunks in x64_backend.cc!
Xbyak::Reg64 X64Emitter::GetContextReg() { return rsi; }
Xbyak::Reg64 X64Emitter::GetMembaseReg() { return rdi; }
Xbyak::Reg64 X64Emitter::GetContextReg() const { return rsi; }
Xbyak::Reg64 X64Emitter::GetMembaseReg() const { return rdi; }
void X64Emitter::ReloadMembase() {
mov(GetMembaseReg(), qword[GetContextReg() + 8]); // membase
mov(GetMembaseReg(),
qword[GetContextReg() +
offsetof(ppc::PPCContext, virtual_membase)]); // membase
}
// Len Assembly Byte Sequence
@@ -917,7 +924,7 @@ static const vec128_t xmm_consts[] = {
/* XMMQNaN */ vec128i(0x7FC00000u),
/* XMMInt127 */ vec128i(0x7Fu),
/* XMM2To32 */ vec128f(0x1.0p32f),
/* xmminf */ vec128i(0x7f800000),
/* XMMFloatInf */ vec128i(0x7f800000),
/* XMMIntsToBytes*/
v128_setr_bytes(0, 4, 8, 12, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
@@ -938,9 +945,7 @@ static const vec128_t xmm_consts[] = {
/*XMMVSRShlByteshuf*/
v128_setr_bytes(13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3, 0x80),
// XMMVSRMask
vec128b(1)
};
vec128b(1)};
void* X64Emitter::FindByteConstantOffset(unsigned bytevalue) {
for (auto& vec : xmm_consts) {
@@ -1347,7 +1352,7 @@ SimdDomain X64Emitter::DeduceSimdDomain(const hir::Value* for_value) {
return SimdDomain::DONTCARE;
}
Xbyak::Address X64Emitter::GetBackendCtxPtr(int offset_in_x64backendctx) {
Xbyak::Address X64Emitter::GetBackendCtxPtr(int offset_in_x64backendctx) const {
/*
index context ptr negatively to get to backend ctx field
*/
@@ -1368,6 +1373,93 @@ Xbyak::Label& X64Emitter::NewCachedLabel() {
label_cache_.push_back(tmp);
return *tmp;
}
template<bool switching_to_fpu>
static void ChangeMxcsrModeDynamicHelper(X64Emitter& e) {
auto flags = e.GetBackendFlagsPtr();
if (switching_to_fpu) {
e.btr(flags, 0); // bit 0 set to 0 = is fpu mode
} else {
e.bts(flags, 0); // bit 0 set to 1 = is vmx mode
}
Xbyak::Label& come_back = e.NewCachedLabel();
Xbyak::Label& reload_bailout =
e.AddToTail([&come_back](X64Emitter& e, Xbyak::Label& thislabel) {
e.L(thislabel);
if (switching_to_fpu) {
e.LoadFpuMxcsrDirect();
} else {
e.LoadVmxMxcsrDirect();
}
e.jmp(come_back, X64Emitter::T_NEAR);
});
if (switching_to_fpu) {
e.jc(reload_bailout,
X64Emitter::T_NEAR); // if carry flag was set, we were VMX mxcsr mode.
} else {
e.jnc(reload_bailout,
X64Emitter::T_NEAR); // if carry flag was set, we were VMX mxcsr mode.
}
e.L(come_back);
}
bool X64Emitter::ChangeMxcsrMode(MXCSRMode new_mode, bool already_set) {
if (new_mode == mxcsr_mode_) {
return false;
}
assert_true(new_mode != MXCSRMode::Unknown);
if (mxcsr_mode_ == MXCSRMode::Unknown) {
// check the mode dynamically
mxcsr_mode_ = new_mode;
if (!already_set) {
if (new_mode == MXCSRMode::Fpu) {
ChangeMxcsrModeDynamicHelper<true>(*this);
} else if (new_mode == MXCSRMode::Vmx) {
ChangeMxcsrModeDynamicHelper<false>(*this);
} else {
assert_unhandled_case(new_mode);
}
} else { //even if already set, we still need to update flags to reflect our mode
if (new_mode == MXCSRMode::Fpu) {
btr(GetBackendFlagsPtr(), 0);
} else if (new_mode == MXCSRMode::Vmx) {
bts(GetBackendFlagsPtr(), 0);
} else {
assert_unhandled_case(new_mode);
}
}
} else {
mxcsr_mode_ = new_mode;
if (!already_set) {
if (new_mode == MXCSRMode::Fpu) {
LoadFpuMxcsrDirect();
btr(GetBackendFlagsPtr(), 0);
return true;
} else if (new_mode == MXCSRMode::Vmx) {
LoadVmxMxcsrDirect();
bts(GetBackendFlagsPtr(), 0);
return true;
} else {
assert_unhandled_case(new_mode);
}
}
}
return false;
}
void X64Emitter::LoadFpuMxcsrDirect() {
vldmxcsr(GetBackendCtxPtr(offsetof(X64BackendContext, mxcsr_fpu)));
}
void X64Emitter::LoadVmxMxcsrDirect() {
vldmxcsr(GetBackendCtxPtr(offsetof(X64BackendContext, mxcsr_vmx)));
}
Xbyak::Address X64Emitter::GetBackendFlagsPtr() const {
Xbyak::Address pt = GetBackendCtxPtr(offsetof(X64BackendContext, flags));
pt.setBit(32);
return pt;
}
} // namespace x64
} // namespace backend
} // namespace cpu

View File

@@ -65,6 +65,12 @@ enum class SimdDomain : uint32_t {
// CONFLICTING means its used in multiple domains)
};
enum class MXCSRMode : uint32_t {
Unknown,
Fpu,
Vmx
};
static SimdDomain PickDomain2(SimdDomain dom1, SimdDomain dom2) {
if (dom1 == dom2) {
return dom1;
@@ -283,8 +289,8 @@ class X64Emitter : public Xbyak::CodeGenerator {
Xbyak::Reg64 GetNativeParam(uint32_t param);
Xbyak::Reg64 GetContextReg();
Xbyak::Reg64 GetMembaseReg();
Xbyak::Reg64 GetContextReg() const;
Xbyak::Reg64 GetMembaseReg() const;
bool CanUseMembaseLow32As0() const { return may_use_membase32_as_zero_reg_; }
void ReloadMembase();
@@ -295,7 +301,7 @@ class X64Emitter : public Xbyak::CodeGenerator {
void MovMem64(const Xbyak::RegExp& addr, uint64_t v);
Xbyak::Address GetXmmConstPtr(XmmConst id);
Xbyak::Address GetBackendCtxPtr(int offset_in_x64backendctx);
Xbyak::Address GetBackendCtxPtr(int offset_in_x64backendctx) const;
void LoadConstantXmm(Xbyak::Xmm dest, float v);
void LoadConstantXmm(Xbyak::Xmm dest, double v);
@@ -304,6 +310,7 @@ class X64Emitter : public Xbyak::CodeGenerator {
Xbyak::Address StashConstantXmm(int index, float v);
Xbyak::Address StashConstantXmm(int index, double v);
Xbyak::Address StashConstantXmm(int index, const vec128_t& v);
Xbyak::Address GetBackendFlagsPtr() const;
void* FindByteConstantOffset(unsigned bytevalue);
void* FindWordConstantOffset(unsigned wordvalue);
void* FindDwordConstantOffset(unsigned bytevalue);
@@ -319,6 +326,16 @@ class X64Emitter : public Xbyak::CodeGenerator {
size_t stack_size() const { return stack_size_; }
SimdDomain DeduceSimdDomain(const hir::Value* for_value);
void ForgetMxcsrMode() {
mxcsr_mode_ = MXCSRMode::Unknown;
}
/*
returns true if had to load mxcsr. DOT_PRODUCT can use this to skip clearing the overflow flag, as it will never be set in the vmx fpscr
*/
bool ChangeMxcsrMode(MXCSRMode new_mode, bool already_set=false);//already_set means that the caller already did vldmxcsr, used for SET_ROUNDING_MODE
void LoadFpuMxcsrDirect(); //unsafe, does not change mxcsr_mode_
void LoadVmxMxcsrDirect(); //unsafe, does not change mxcsr_mode_
protected:
void* Emplace(const EmitFunctionInfo& func_info,
GuestFunction* function = nullptr);
@@ -359,6 +376,7 @@ class X64Emitter : public Xbyak::CodeGenerator {
std::vector<Xbyak::Label*>
label_cache_; // for creating labels that need to be referenced much
// later by tail emitters
MXCSRMode mxcsr_mode_ = MXCSRMode::Unknown;
};
} // namespace x64

View File

@@ -616,7 +616,31 @@ struct Sequence {
}
}
};
template <typename T>
static Xmm GetInputRegOrConstant(X64Emitter& e, const T& input,
Xmm xmm_to_use_if_const) {
if (input.is_constant) {
using constant_type = std::remove_reference_t<decltype(input.constant())>;
if constexpr (std::is_integral_v<constant_type>) {
vec128_t input_constant = vec128b(0);
if constexpr (sizeof(constant_type) == 4) {
input_constant.i32[0] = input.constant();
} else if constexpr (sizeof(constant_type) == 8) {
input_constant.low = input.constant();
} else {
assert_unhandled_case(sizeof(constant_type));
}
e.LoadConstantXmm(xmm_to_use_if_const, input_constant);
} else {
e.LoadConstantXmm(xmm_to_use_if_const, input.constant());
}
return xmm_to_use_if_const;
} else {
return input;
}
}
} // namespace x64
} // namespace backend
} // namespace cpu

View File

@@ -257,6 +257,7 @@ struct CALL_TRUE_I8
e.jz(skip);
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
e.L(skip);
e.ForgetMxcsrMode();
}
};
struct CALL_TRUE_I16
@@ -268,6 +269,7 @@ struct CALL_TRUE_I16
e.jz(skip);
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
e.L(skip);
e.ForgetMxcsrMode();
}
};
struct CALL_TRUE_I32
@@ -279,6 +281,7 @@ struct CALL_TRUE_I32
e.jz(skip);
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
e.L(skip);
e.ForgetMxcsrMode();
}
};
struct CALL_TRUE_I64
@@ -290,6 +293,7 @@ struct CALL_TRUE_I64
e.jz(skip);
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
e.L(skip);
e.ForgetMxcsrMode();
}
};
struct CALL_TRUE_F32
@@ -301,6 +305,7 @@ struct CALL_TRUE_F32
e.jz(skip);
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
e.L(skip);
e.ForgetMxcsrMode();
}
};
@@ -313,6 +318,7 @@ struct CALL_TRUE_F64
e.jz(skip);
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
e.L(skip);
e.ForgetMxcsrMode();
}
};
EMITTER_OPCODE_TABLE(OPCODE_CALL_TRUE, CALL_TRUE_I8, CALL_TRUE_I16,
@@ -326,6 +332,7 @@ struct CALL_INDIRECT
: Sequence<CALL_INDIRECT, I<OPCODE_CALL_INDIRECT, VoidOp, I64Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.CallIndirect(i.instr, i.src1);
e.ForgetMxcsrMode();
}
};
EMITTER_OPCODE_TABLE(OPCODE_CALL_INDIRECT, CALL_INDIRECT);

View File

@@ -16,7 +16,13 @@
// For OPCODE_PACK/OPCODE_UNPACK
#include "third_party/half/include/half.hpp"
#include "xenia/base/cvar.h"
#include "xenia/cpu/backend/x64/x64_stack_layout.h"
DEFINE_bool(use_extended_range_half, true,
"Emulate extended range half-precision, may be slower on games "
"that use it heavily",
"CPU");
namespace xe {
namespace cpu {
namespace backend {
@@ -31,6 +37,8 @@ struct VECTOR_CONVERT_I2F
: Sequence<VECTOR_CONVERT_I2F,
I<OPCODE_VECTOR_CONVERT_I2F, V128Op, V128Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.ChangeMxcsrMode(MXCSRMode::Vmx);
Xmm src1 = GetInputRegOrConstant(e, i.src1, e.xmm3);
// flags = ARITHMETIC_UNSIGNED
if (i.instr->flags & ARITHMETIC_UNSIGNED) {
// Round manually to (1.stored mantissa bits * 2^31) or to 2^32 to the
@@ -46,8 +54,8 @@ struct VECTOR_CONVERT_I2F
// be 4294967296.0f.
// xmm0 = src + 0b01111111 + ((src >> 8) & 1)
// (xmm1 also used to launch reg + mem early and to require it late)
e.vpaddd(e.xmm1, i.src1, e.GetXmmConstPtr(XMMInt127));
e.vpslld(e.xmm0, i.src1, 31 - 8);
e.vpaddd(e.xmm1, src1, e.GetXmmConstPtr(XMMInt127));
e.vpslld(e.xmm0, src1, 31 - 8);
e.vpsrld(e.xmm0, e.xmm0, 31);
e.vpaddd(e.xmm0, e.xmm0, e.xmm1);
// xmm0 = (0xFF800000 | 23 explicit mantissa bits), or 0 if overflowed
@@ -63,13 +71,13 @@ struct VECTOR_CONVERT_I2F
// Convert from signed integer to float.
// xmm1 = [0x00000000, 0x7FFFFFFF] case result
e.vcvtdq2ps(e.xmm1, i.src1);
e.vcvtdq2ps(e.xmm1, src1);
// Merge the two ways depending on whether the number is >= 0x80000000
// (has high bit set).
e.vblendvps(i.dest, e.xmm1, e.xmm0, i.src1);
e.vblendvps(i.dest, e.xmm1, e.xmm0, src1);
} else {
e.vcvtdq2ps(i.dest, i.src1);
e.vcvtdq2ps(i.dest, src1);
}
}
};
@@ -82,9 +90,11 @@ struct VECTOR_CONVERT_F2I
: Sequence<VECTOR_CONVERT_F2I,
I<OPCODE_VECTOR_CONVERT_F2I, V128Op, V128Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.ChangeMxcsrMode(MXCSRMode::Vmx);
Xmm src1 = GetInputRegOrConstant(e, i.src1, e.xmm3);
if (i.instr->flags & ARITHMETIC_UNSIGNED) {
// clamp to min 0
e.vmaxps(e.xmm0, i.src1, e.GetXmmConstPtr(XMMZero));
e.vmaxps(e.xmm0, src1, e.GetXmmConstPtr(XMMZero));
// xmm1 = mask of values >= (unsigned)INT_MIN
e.vcmpgeps(e.xmm1, e.xmm0, e.GetXmmConstPtr(XMMPosIntMinPS));
@@ -108,14 +118,14 @@ struct VECTOR_CONVERT_F2I
e.vpor(i.dest, i.dest, e.xmm0);
} else {
// xmm2 = NaN mask
e.vcmpunordps(e.xmm2, i.src1, i.src1);
e.vcmpunordps(e.xmm2, src1, src1);
// convert packed floats to packed dwords
e.vcvttps2dq(e.xmm0, i.src1);
e.vcvttps2dq(e.xmm0, src1);
// (high bit) xmm1 = dest is indeterminate and i.src1 >= 0
e.vpcmpeqd(e.xmm1, e.xmm0, e.GetXmmConstPtr(XMMIntMin));
e.vpandn(e.xmm1, i.src1, e.xmm1);
e.vpandn(e.xmm1, src1, e.xmm1);
// saturate positive values
e.vblendvps(i.dest, e.xmm0, e.GetXmmConstPtr(XMMIntMax), e.xmm1);
@@ -131,6 +141,7 @@ struct VECTOR_DENORMFLUSH
: Sequence<VECTOR_DENORMFLUSH,
I<OPCODE_VECTOR_DENORMFLUSH, V128Op, V128Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.ChangeMxcsrMode(MXCSRMode::Vmx);
e.vxorps(e.xmm1, e.xmm1, e.xmm1); // 0.25 P0123
e.vandps(e.xmm0, i.src1,
@@ -352,6 +363,7 @@ struct VECTOR_COMPARE_EQ_V128
e.vpcmpeqd(dest, src1, src2);
break;
case FLOAT32_TYPE:
e.ChangeMxcsrMode(MXCSRMode::Vmx);
e.vcmpeqps(dest, src1, src2);
break;
}
@@ -380,6 +392,7 @@ struct VECTOR_COMPARE_SGT_V128
e.vpcmpgtd(dest, src1, src2);
break;
case FLOAT32_TYPE:
e.ChangeMxcsrMode(MXCSRMode::Vmx);
e.vcmpgtps(dest, src1, src2);
break;
}
@@ -414,6 +427,7 @@ struct VECTOR_COMPARE_SGE_V128
e.vpor(dest, e.xmm0);
break;
case FLOAT32_TYPE:
e.ChangeMxcsrMode(MXCSRMode::Vmx);
e.vcmpgeps(dest, src1, src2);
break;
}
@@ -441,6 +455,7 @@ struct VECTOR_COMPARE_UGT_V128
sign_addr = e.GetXmmConstPtr(XMMSignMaskI32);
break;
case FLOAT32_TYPE:
e.ChangeMxcsrMode(MXCSRMode::Vmx);
sign_addr = e.GetXmmConstPtr(XMMSignMaskF32);
break;
default:
@@ -498,6 +513,7 @@ struct VECTOR_COMPARE_UGE_V128
sign_addr = e.GetXmmConstPtr(XMMSignMaskI32);
break;
case FLOAT32_TYPE:
e.ChangeMxcsrMode(MXCSRMode::Vmx);
sign_addr = e.GetXmmConstPtr(XMMSignMaskF32);
break;
}
@@ -620,6 +636,7 @@ struct VECTOR_ADD
case FLOAT32_TYPE:
assert_false(is_unsigned);
assert_false(saturate);
e.ChangeMxcsrMode(MXCSRMode::Vmx);
e.vaddps(dest, src1, src2);
break;
default:
@@ -711,6 +728,7 @@ struct VECTOR_SUB
}
break;
case FLOAT32_TYPE:
e.ChangeMxcsrMode(MXCSRMode::Vmx);
e.vsubps(dest, src1, src2);
break;
default:
@@ -2003,6 +2021,7 @@ EMITTER_OPCODE_TABLE(OPCODE_SWIZZLE, SWIZZLE);
// ============================================================================
struct PACK : Sequence<PACK, I<OPCODE_PACK, V128Op, V128Op, V128Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.ChangeMxcsrMode(MXCSRMode::Vmx);
switch (i.instr->flags & PACK_TYPE_MODE) {
case PACK_TYPE_D3DCOLOR:
EmitD3DCOLOR(e, i);
@@ -2062,9 +2081,14 @@ struct PACK : Sequence<PACK, I<OPCODE_PACK, V128Op, V128Op, V128Op>> {
alignas(16) uint16_t b[8];
_mm_store_ps(a, src1);
std::memset(b, 0, sizeof(b));
for (int i = 0; i < 2; i++) {
b[7 - i] = half_float::detail::float2half<std::round_toward_zero>(a[i]);
if (!cvars::use_extended_range_half) {
for (int i = 0; i < 2; i++) {
b[7 - i] = half_float::detail::float2half<std::round_toward_zero>(a[i]);
}
} else {
for (int i = 0; i < 2; i++) {
b[7 - i] = float_to_xenos_half(a[i]);
}
}
return _mm_load_si128(reinterpret_cast<__m128i*>(b));
@@ -2074,7 +2098,7 @@ struct PACK : Sequence<PACK, I<OPCODE_PACK, V128Op, V128Op, V128Op>> {
// http://blogs.msdn.com/b/chuckw/archive/2012/09/11/directxmath-f16c-and-fma.aspx
// dest = [(src1.x | src1.y), 0, 0, 0]
if (e.IsFeatureEnabled(kX64EmitF16C)) {
if (e.IsFeatureEnabled(kX64EmitF16C) && !cvars::use_extended_range_half) {
Xmm src;
if (i.src1.is_constant) {
src = i.dest;
@@ -2101,10 +2125,15 @@ struct PACK : Sequence<PACK, I<OPCODE_PACK, V128Op, V128Op, V128Op>> {
alignas(16) uint16_t b[8];
_mm_store_ps(a, src1);
std::memset(b, 0, sizeof(b));
for (int i = 0; i < 4; i++) {
b[7 - (i ^ 2)] =
half_float::detail::float2half<std::round_toward_zero>(a[i]);
if (!cvars::use_extended_range_half) {
for (int i = 0; i < 4; i++) {
b[7 - (i ^ 2)] =
half_float::detail::float2half<std::round_toward_zero>(a[i]);
}
} else {
for (int i = 0; i < 4; i++) {
b[7 - (i ^ 2)] = float_to_xenos_half(a[i]);
}
}
return _mm_load_si128(reinterpret_cast<__m128i*>(b));
@@ -2113,7 +2142,7 @@ struct PACK : Sequence<PACK, I<OPCODE_PACK, V128Op, V128Op, V128Op>> {
assert_true(i.src2.value->IsConstantZero());
// dest = [(src1.z | src1.w), (src1.x | src1.y), 0, 0]
if (e.IsFeatureEnabled(kX64EmitF16C)) {
if (e.IsFeatureEnabled(kX64EmitF16C) && !cvars::use_extended_range_half) {
Xmm src;
if (i.src1.is_constant) {
src = i.dest;
@@ -2420,6 +2449,7 @@ EMITTER_OPCODE_TABLE(OPCODE_PACK, PACK);
// ============================================================================
struct UNPACK : Sequence<UNPACK, I<OPCODE_UNPACK, V128Op, V128Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.ChangeMxcsrMode(MXCSRMode::Vmx);
switch (i.instr->flags & PACK_TYPE_MODE) {
case PACK_TYPE_D3DCOLOR:
EmitD3DCOLOR(e, i);
@@ -2478,10 +2508,15 @@ struct UNPACK : Sequence<UNPACK, I<OPCODE_UNPACK, V128Op, V128Op>> {
alignas(16) float b[4];
_mm_store_si128(reinterpret_cast<__m128i*>(a), src1);
for (int i = 0; i < 2; i++) {
b[i] = half_float::detail::half2float(a[VEC128_W(6 + i)]);
if (!cvars::use_extended_range_half) {
for (int i = 0; i < 2; i++) {
b[i] = half_float::detail::half2float(a[VEC128_W(6 + i)]);
}
} else {
for (int i = 0; i < 2; i++) {
b[i] = xenos_half_to_float(a[VEC128_W(6 + i)]);
}
}
// Constants, or something
b[2] = 0.f;
b[3] = 1.f;
@@ -2501,7 +2536,9 @@ struct UNPACK : Sequence<UNPACK, I<OPCODE_UNPACK, V128Op, V128Op>> {
// Also zero out the high end.
// TODO(benvanik): special case constant unpacks that just get 0/1/etc.
if (e.IsFeatureEnabled(kX64EmitF16C)) {
if (e.IsFeatureEnabled(kX64EmitF16C) &&
!cvars::use_extended_range_half) { // todo: can use cvtph and bit logic
// to implement
Xmm src;
if (i.src1.is_constant) {
src = i.dest;
@@ -2534,16 +2571,21 @@ struct UNPACK : Sequence<UNPACK, I<OPCODE_UNPACK, V128Op, V128Op>> {
alignas(16) uint16_t a[8];
alignas(16) float b[4];
_mm_store_si128(reinterpret_cast<__m128i*>(a), src1);
for (int i = 0; i < 4; i++) {
b[i] = half_float::detail::half2float(a[VEC128_W(4 + i)]);
if (!cvars::use_extended_range_half) {
for (int i = 0; i < 4; i++) {
b[i] = half_float::detail::half2float(a[VEC128_W(4 + i)]);
}
} else {
for (int i = 0; i < 4; i++) {
b[i] = xenos_half_to_float(a[VEC128_W(4 + i)]);
}
}
return _mm_load_ps(b);
}
static void EmitFLOAT16_4(X64Emitter& e, const EmitArgType& i) {
// src = [(dest.x | dest.y), (dest.z | dest.w), 0, 0]
if (e.IsFeatureEnabled(kX64EmitF16C)) {
if (e.IsFeatureEnabled(kX64EmitF16C) && !cvars::use_extended_range_half) {
Xmm src;
if (i.src1.is_constant) {
src = i.dest;
@@ -2805,6 +2847,32 @@ struct UNPACK : Sequence<UNPACK, I<OPCODE_UNPACK, V128Op, V128Op>> {
};
EMITTER_OPCODE_TABLE(OPCODE_UNPACK, UNPACK);
struct SET_NJM_I8 : Sequence<SET_NJM_I8, I<OPCODE_SET_NJM, VoidOp, I8Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
auto addr_vmx = e.GetBackendCtxPtr(offsetof(X64BackendContext, mxcsr_vmx));
addr_vmx.setBit(32);
if (i.src1.is_constant) {
if (i.src1.constant() == 0) {
// turn off daz/flush2z
e.mov(addr_vmx, _MM_MASK_MASK);
} else {
e.mov(addr_vmx, DEFAULT_VMX_MXCSR);
}
} else {
e.test(i.src1, i.src1);
e.mov(e.edx, DEFAULT_VMX_MXCSR);
e.mov(e.eax, _MM_MASK_MASK);
e.cmove(e.edx, e.eax);
e.mov(addr_vmx, e.edx);
}
e.ChangeMxcsrMode(MXCSRMode::Vmx);
}
};
EMITTER_OPCODE_TABLE(OPCODE_SET_NJM, SET_NJM_I8);
} // namespace x64
} // namespace backend
} // namespace cpu

File diff suppressed because it is too large Load Diff