From 927e2492ef720d2223bd8b149a02af875e11c398 Mon Sep 17 00:00:00 2001 From: Walter <90877047+WalterKruger@users.noreply.github.com> Date: Mon, 22 Jun 2026 22:27:37 +1000 Subject: [PATCH] SPU LLVM: Calculate FREST fraction LUT using vperm2dlculate `FREST` fraction using `vperm2d` for AVX512 (#18931) The `FREST` instruction uses a scalar lookup-table loop to calculate the mantissa. Given that it's only 32 elements, we can load it into two 512-bit vectors and treat them as the sources to the `vperm2d` shuffle instruction. This allows it to be calculated in parallel, improving its performance. --- rpcs3/Emu/CPU/CPUTranslator.h | 17 ++++++++++++ rpcs3/Emu/Cell/SPULLVMRecompiler.cpp | 40 +++++++++++++++++++++------- 2 files changed, 47 insertions(+), 10 deletions(-) diff --git a/rpcs3/Emu/CPU/CPUTranslator.h b/rpcs3/Emu/CPU/CPUTranslator.h index 0ae5179916..97020f039a 100644 --- a/rpcs3/Emu/CPU/CPUTranslator.h +++ b/rpcs3/Emu/CPU/CPUTranslator.h @@ -3670,6 +3670,23 @@ public: #endif } + template + value_t vperm2d128From512(T1 a, T2 b, T3 c) + { + value_t result; + value_t perm512; + + const auto data0 = a.eval(m_ir); + const auto index128 = b.eval(m_ir); + const auto data1 = c.eval(m_ir); + + const auto index512 = m_ir->CreateInsertVector(get_type(), llvm::UndefValue::get(get_type()), index128, m_ir->getInt64(0)); + perm512.value = m_ir->CreateCall(get_intrinsic(llvm::Intrinsic::x86_avx512_vpermi2var_d_512), {data0, index512, data1}); + + result.value = m_ir->CreateExtractVector(get_type(), perm512.value, m_ir->getInt64(0)); + return result; + } + template value_t gf2p8affineqb(T1 a, T2 b, u8 c) { diff --git a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp index 5f5c90f6f4..392f2f339b 100644 --- a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp @@ -7498,13 +7498,23 @@ public: const auto a_sign = (a & splat(0x80000000)); value_t final_result = eval(splat(0)); - for (u32 i = 0; i < 4; i++) + if (m_use_avx512) { - const auto eval_fraction = eval(extract(a_fraction, i)); + value_t lo_lut; + value_t hi_lut; + lo_lut.value = llvm::ConstantDataVector::get(m_context, llvm::ArrayRef(spu_frest_fraction_lut, 16)); + hi_lut.value = llvm::ConstantDataVector::get(m_context, llvm::ArrayRef(spu_frest_fraction_lut + 16, 16)); - value_t r_fraction = load_const(m_spu_frest_fraction_lut, eval_fraction); - - final_result = eval(insert(final_result, i, r_fraction)); + final_result = vperm2d128From512(lo_lut, a_fraction, hi_lut); + } + else + { + for (u32 i = 0; i < 4; i++) + { + const auto eval_fraction = eval(extract(a_fraction, i)); + value_t r_fraction = load_const(m_spu_frest_fraction_lut, eval_fraction); + final_result = eval(insert(final_result, i, r_fraction)); + } } //final_result = eval(select(final_result != (0), final_result, bitcast(pshufb(splat(0), bitcast(final_result))))); @@ -8551,13 +8561,23 @@ public: const auto a_sign = (a & splat(0x80000000)); value_t b = eval(splat(0)); - for (u32 i = 0; i < 4; i++) + if (m_use_avx512) { - const auto eval_fraction = eval(extract(a_fraction, i)); + value_t lo_lut; + value_t hi_lut; + lo_lut.value = llvm::ConstantDataVector::get(m_context, llvm::ArrayRef(spu_frest_fraction_lut, 16)); + hi_lut.value = llvm::ConstantDataVector::get(m_context, llvm::ArrayRef(spu_frest_fraction_lut + 16, 16)); - value_t r_fraction = load_const(m_spu_frest_fraction_lut, eval_fraction); - - b = eval(insert(b, i, r_fraction)); + b = vperm2d128From512(lo_lut, a_fraction, hi_lut); + } + else + { + for (u32 i = 0; i < 4; i++) + { + const auto eval_fraction = eval(extract(a_fraction, i)); + value_t r_fraction = load_const(m_spu_frest_fraction_lut, eval_fraction); + b = eval(insert(b, i, r_fraction)); + } } b = eval(b | fix_exponent | a_sign);