From b0cc3db4d869461a1a53c6b3d6fdd1cb8bcb55dd Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Mon, 5 Sep 2022 08:17:33 -0700 Subject: [PATCH 1/4] [x64] Add AVX512 optimization for `NOT_V128` --- src/xenia/cpu/backend/x64/x64_sequences.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/xenia/cpu/backend/x64/x64_sequences.cc b/src/xenia/cpu/backend/x64/x64_sequences.cc index c7ef4361b..135a3753c 100644 --- a/src/xenia/cpu/backend/x64/x64_sequences.cc +++ b/src/xenia/cpu/backend/x64/x64_sequences.cc @@ -2891,6 +2891,10 @@ struct NOT_I64 : Sequence> { }; struct NOT_V128 : Sequence> { static void Emit(X64Emitter& e, const EmitArgType& i) { + if (e.IsFeatureEnabled(kX64EmitAVX512Ortho)) { + e.vpternlogd(i.dest, i.src1, i.src1, 0b01010101); + return; + } // dest = src ^ 0xFFFF... e.vpxor(i.dest, i.src1, e.GetXmmConstPtr(XMMFFFF /* FF... */)); } From 90fffe1de7ca2f6ee404d9fe3181b02a6e316ac8 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Mon, 5 Sep 2022 09:54:37 -0700 Subject: [PATCH 2/4] [PPC] Fix memory assert formatting This was still using printf-style format specifiers. Causing memory asserts to show up like this while testing. ``` !> 0000438C Memory 10001040 assert failed: !> 0000438C Expected: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X !> 0000438C Actual: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X !> 0000438C TEST FAILED ``` Updated them so they format correctly: ``` !> 00002CCC Memory 10001040 assert failed: !> 00002CCC Expected: FC FD FE FF 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F !> 00002CCC Actual: FC FD FE FF 00 00 00 00 00 00 00 00 00 00 00 00 !> 00002CCC TEST FAILED ``` --- src/xenia/cpu/ppc/testing/ppc_testing_main.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/xenia/cpu/ppc/testing/ppc_testing_main.cc b/src/xenia/cpu/ppc/testing/ppc_testing_main.cc index bcc7e0c6c..5faa4998e 100644 --- a/src/xenia/cpu/ppc/testing/ppc_testing_main.cc +++ b/src/xenia/cpu/ppc/testing/ppc_testing_main.cc @@ -349,8 +349,8 @@ class TestRunner { uint32_t expected = std::strtoul(ccs, nullptr, 16); uint8_t actual = *p; - expecteds.AppendFormat(" %02X", expected); - actuals.AppendFormat(" %02X", actual); + expecteds.AppendFormat(" {:02X}", expected); + actuals.AppendFormat(" {:02X}", actual); if (expected != actual) { any_failed = true; From 9fd684594b6e7f0fe18745200482dedb423c327a Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Fri, 9 Sep 2022 14:16:19 -0700 Subject: [PATCH 3/4] [x64] Add AVX512 optimization for `OPCODE_VECTOR_CONVERT_F2I`(unsigned) `vcvttps2udq` already saturates overflowing and unordered values to `0xFFFFFFFF`. Using mask registers, zeroes are written to negative values within the same instruction. --- src/xenia/cpu/backend/x64/x64_seq_vector.cc | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/xenia/cpu/backend/x64/x64_seq_vector.cc b/src/xenia/cpu/backend/x64/x64_seq_vector.cc index 09eb2b00e..820e6bf91 100644 --- a/src/xenia/cpu/backend/x64/x64_seq_vector.cc +++ b/src/xenia/cpu/backend/x64/x64_seq_vector.cc @@ -83,6 +83,19 @@ struct VECTOR_CONVERT_F2I I> { static void Emit(X64Emitter& e, const EmitArgType& i) { if (i.instr->flags & ARITHMETIC_UNSIGNED) { + if (e.IsFeatureEnabled(kX64EmitAVX512Ortho)) { + Opmask mask = e.k1; + // Mask positive values and unordered values + // _CMP_NLT_UQ + e.vcmpps(mask, i.src1, e.GetXmmConstPtr(XMMZero), 0x15); + + // vcvttps2udq will saturate overflowing positive values and unordered + // values to UINT_MAX. Mask registers will write zero everywhere + // else (negative values) + e.vcvttps2udq(i.dest.reg() | mask | e.T_z, i.src1); + return; + } + // clamp to min 0 e.vmaxps(e.xmm0, i.src1, e.GetXmmConstPtr(XMMZero)); From addd8c94e552fac386eced5411487d816afcffe8 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Fri, 9 Sep 2022 15:59:16 -0700 Subject: [PATCH 4/4] [x64] Add AVX512 optimization for `OPCODE_VECTOR_ADD`(saturated) Uses a single `vpternlogd` to test for signed/unsigned overflow/underflow. Then utilizes AVX512 mask operations to create either `0x7FFFFFFF` or `0x80000000` arithmetically. --- src/xenia/cpu/backend/x64/x64_seq_vector.cc | 23 +++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/xenia/cpu/backend/x64/x64_seq_vector.cc b/src/xenia/cpu/backend/x64/x64_seq_vector.cc index 820e6bf91..2dfbe8402 100644 --- a/src/xenia/cpu/backend/x64/x64_seq_vector.cc +++ b/src/xenia/cpu/backend/x64/x64_seq_vector.cc @@ -560,6 +560,15 @@ struct VECTOR_ADD case INT32_TYPE: if (saturate) { if (is_unsigned) { + if (e.IsFeatureEnabled(kX64EmitAVX512Ortho)) { + e.vpaddd(dest, src1, src2); + Opmask saturate = e.k1; + // _mm_cmplt_epu32_mask + e.vpcmpud(saturate, dest, src1, 0x1); + e.vpternlogd(dest | saturate, dest, dest, 0xFF); + return; + } + // xmm0 is the only temp register that can be used by // src1/src2. e.vpaddd(e.xmm1, src1, src2); @@ -575,6 +584,20 @@ struct VECTOR_ADD } else { e.vpaddd(e.xmm1, src1, src2); + if (e.IsFeatureEnabled(kX64EmitAVX512Ortho | + kX64EmitAVX512DQ)) { + e.vmovdqa32(e.xmm3, src1); + e.vpternlogd(e.xmm3, e.xmm1, src2, 0b00100100); + + const Opmask saturate = e.k1; + e.vpmovd2m(saturate, e.xmm3); + + e.vpsrad(e.xmm2, e.xmm1, 31); + e.vpxord(e.xmm2, e.xmm2, e.GetXmmConstPtr(XMMSignMaskI32)); + e.vpblendmd(dest | saturate, e.xmm1, e.xmm2); + return; + } + // Overflow results if two inputs are the same sign and the // result isn't the same sign. if ((s32b)(~(src1 ^ src2) & // (src1 ^ res)) < 0) then overflowed