From 7ff152a5a7f69390c1a625d3fdc57681d5ccb7e9 Mon Sep 17 00:00:00 2001 From: Gliniak <153369+Gliniak@users.noreply.github.com> Date: Sun, 5 Jul 2026 17:53:43 +0200 Subject: [PATCH] [X64] MAX_V128/MIN_V128 Proper NaN handling. According to console. FFC00000->FFC00000 instead of FFFFFC00 as it was previously. Test case: 415607E8 - "Evil Queen's Castle" level --- src/xenia/cpu/backend/x64/x64_sequences.cc | 26 ++++++++++++++----- src/xenia/cpu/ppc/testing/instr__gen_vmaxfp.s | 17 ++++++++++++ src/xenia/cpu/ppc/testing/instr__gen_vminfp.s | 17 ++++++++++++ 3 files changed, 54 insertions(+), 6 deletions(-) diff --git a/src/xenia/cpu/backend/x64/x64_sequences.cc b/src/xenia/cpu/backend/x64/x64_sequences.cc index c216a8f25..33b86a213 100644 --- a/src/xenia/cpu/backend/x64/x64_sequences.cc +++ b/src/xenia/cpu/backend/x64/x64_sequences.cc @@ -601,11 +601,18 @@ struct MAX_V128 : Sequence> { static void Emit(X64Emitter& e, const EmitArgType& i) { e.ChangeMxcsrMode(MXCSRMode::Vmx); // if 0 and -0, return 0! opposite of minfp - auto src1 = GetInputRegOrConstant(e, i.src1, e.xmm0); - auto src2 = GetInputRegOrConstant(e, i.src2, e.xmm1); + const Xmm src1 = GetInputRegOrConstant(e, i.src1, e.xmm0); + const Xmm src2 = GetInputRegOrConstant(e, i.src2, e.xmm1); + e.vmaxps(e.xmm2, src1, src2); e.vmaxps(e.xmm3, src2, src1); - e.vorps(i.dest, e.xmm2, e.xmm3); + e.vandps(e.xmm2, e.xmm2, e.xmm3); + + e.vcmpunordps(e.xmm3, src1, src1); // mask: vA is NaN + e.vblendvps(e.xmm3, src2, src1, e.xmm3); + + e.vcmpunordps(i.dest, src1, src2); // mask: vA or vB is NaN + e.vblendvps(i.dest, e.xmm2, e.xmm3, i.dest); } }; EMITTER_OPCODE_TABLE(OPCODE_MAX, MAX_F32, MAX_F64, MAX_V128); @@ -660,11 +667,18 @@ struct MIN_F64 : Sequence> { struct MIN_V128 : Sequence> { static void Emit(X64Emitter& e, const EmitArgType& i) { e.ChangeMxcsrMode(MXCSRMode::Vmx); - auto src1 = GetInputRegOrConstant(e, i.src1, e.xmm0); - auto src2 = GetInputRegOrConstant(e, i.src2, e.xmm1); + const Xmm src1 = GetInputRegOrConstant(e, i.src1, e.xmm0); + const Xmm src2 = GetInputRegOrConstant(e, i.src2, e.xmm1); + e.vminps(e.xmm2, src1, src2); e.vminps(e.xmm3, src2, src1); - e.vorps(i.dest, e.xmm2, e.xmm3); + e.vorps(e.xmm2, e.xmm2, e.xmm3); + + e.vcmpunordps(e.xmm3, src1, src1); // mask: vA is NaN + e.vblendvps(e.xmm3, src2, src1, e.xmm3); + + e.vcmpunordps(i.dest, src1, src2); // mask: vA or vB is NaN + e.vblendvps(i.dest, e.xmm2, e.xmm3, i.dest); } }; EMITTER_OPCODE_TABLE(OPCODE_MIN, MIN_I8, MIN_I16, MIN_I32, MIN_I64, MIN_F32, diff --git a/src/xenia/cpu/ppc/testing/instr__gen_vmaxfp.s b/src/xenia/cpu/ppc/testing/instr__gen_vmaxfp.s index 3e2be6cd8..d731cfe89 100644 --- a/src/xenia/cpu/ppc/testing/instr__gen_vmaxfp.s +++ b/src/xenia/cpu/ppc/testing/instr__gen_vmaxfp.s @@ -5848,3 +5848,20 @@ test_vmaxfp_650_GEN: #_ REGISTER_OUT v2 [FFFFFF80, 0000007F, FFFEFF7F, 00010080] #_ REGISTER_OUT v3 [FFFFFFFF, FFFFFFFF, FFFFFFFF, FFFFFFFF] +test_vmaxfp_651_GEN: + #_ REGISTER_IN v1 [FFC00000, FFC00000, FFC00000, 00000000] + #_ REGISTER_IN v2 [00000000, 00000000, 00000000, 00000000] + vmaxfp v3, v1, v2 + blr + #_ REGISTER_OUT v1 [FFC00000, FFC00000, FFC00000, 00000000] + #_ REGISTER_OUT v2 [00000000, 00000000, 00000000, 00000000] + #_ REGISTER_OUT v3 [FFC00000, FFC00000, FFC00000, 00000000] + +test_vmaxfp_652_GEN: + #_ REGISTER_IN v1 [7FC00000, 7FC00000, 7FC00000, 00000000] + #_ REGISTER_IN v2 [00000000, 00000000, 00000000, 00000000] + vmaxfp v3, v1, v2 + blr + #_ REGISTER_OUT v1 [7FC00000, 7FC00000, 7FC00000, 00000000] + #_ REGISTER_OUT v2 [00000000, 00000000, 00000000, 00000000] + #_ REGISTER_OUT v3 [7FC00000, 7FC00000, 7FC00000, 00000000] diff --git a/src/xenia/cpu/ppc/testing/instr__gen_vminfp.s b/src/xenia/cpu/ppc/testing/instr__gen_vminfp.s index 0199d59ee..c5ebd0be7 100644 --- a/src/xenia/cpu/ppc/testing/instr__gen_vminfp.s +++ b/src/xenia/cpu/ppc/testing/instr__gen_vminfp.s @@ -5848,3 +5848,20 @@ test_vminfp_650_GEN: #_ REGISTER_OUT v2 [FFFFFF80, 0000007F, FFFEFF7F, 00010080] #_ REGISTER_OUT v3 [FFFFFFFF, FFFFFFFF, FFFFFFFF, FFFFFFFF] +test_vminfp_651_GEN: + #_ REGISTER_IN v1 [FFC00000, FFC00000, FFC00000, 00000000] + #_ REGISTER_IN v2 [477FFC00, 477FFC00, 477FFC00, 477FFC00] + vminfp v3, v1, v2 + blr + #_ REGISTER_OUT v1 [FFC00000, FFC00000, FFC00000, 00000000] + #_ REGISTER_OUT v2 [477FFC00, 477FFC00, 477FFC00, 477FFC00] + #_ REGISTER_OUT v3 [FFC00000, FFC00000, FFC00000, 00000000] + +test_vminfp_652_GEN: + #_ REGISTER_IN v1 [7FC00000, 7FC00000, 7FC00000, 00000000] + #_ REGISTER_IN v2 [477FFC00, 477FFC00, 477FFC00, 477FFC00] + vminfp v3, v1, v2 + blr + #_ REGISTER_OUT v1 [7FC00000, 7FC00000, 7FC00000, 00000000] + #_ REGISTER_OUT v2 [477FFC00, 477FFC00, 477FFC00, 477FFC00] + #_ REGISTER_OUT v3 [7FC00000, 7FC00000, 7FC00000, 00000000]