diff --git a/rpcs3/Emu/CPU/CPUTranslator.cpp b/rpcs3/Emu/CPU/CPUTranslator.cpp index 66b5c69af0..4b37001c83 100644 --- a/rpcs3/Emu/CPU/CPUTranslator.cpp +++ b/rpcs3/Emu/CPU/CPUTranslator.cpp @@ -207,6 +207,16 @@ void cpu_translator::initialize(llvm::LLVMContext& context, llvm::ExecutionEngin { m_use_dotprod = true; } + + if (utils::has_sve() && utils::sve_length() == 128) + { + m_use_sve_128 = true; + } + + if (utils::has_sve2() && utils::sve_length() == 128) + { + m_use_sve2_128 = true; + } #endif } diff --git a/rpcs3/Emu/CPU/CPUTranslator.h b/rpcs3/Emu/CPU/CPUTranslator.h index 46c520a0a3..5194f05e63 100644 --- a/rpcs3/Emu/CPU/CPUTranslator.h +++ b/rpcs3/Emu/CPU/CPUTranslator.h @@ -3123,6 +3123,10 @@ protected: // Allow direct TBL2/TBX2 emission. bool m_use_tbl2 = true; + + bool m_use_sve_128 = false; + + bool m_use_sve2_128 = false; #else // Allow FMA bool m_use_fma = false; @@ -3747,6 +3751,45 @@ template result.value = m_ir->CreateCall(get_intrinsic(llvm::Intrinsic::aarch64_neon_umull), {data0, data1}); return result; } + + llvm::Value* to_sve_vector(llvm::Value* value) + { + if (llvm::isa(value->getType())) + { + return value; + } + + const auto fixed_type = llvm::cast(value->getType()); + const auto scalable_type = llvm::ScalableVectorType::get(fixed_type->getElementType(), fixed_type->getNumElements()); + return m_ir->CreateInsertVector(scalable_type, llvm::UndefValue::get(scalable_type), value, m_ir->getInt64(0)); + } + + llvm::Value* from_sve_vector(llvm::Value* value, llvm::FixedVectorType* fixed_type) + { + if (value->getType() == fixed_type) + { + return value; + } + + return m_ir->CreateExtractVector(fixed_type, value, m_ir->getInt64(0)); + } + + llvm::Value* sve_ptrue(llvm::FixedVectorType* fixed_type) + { + const auto pred_type = llvm::ScalableVectorType::get(m_ir->getInt1Ty(), fixed_type->getNumElements()); + return m_ir->CreateIntrinsic(llvm::Intrinsic::aarch64_sve_ptrue, {pred_type}, {m_ir->getInt32(31)}); + } + + llvm::Value* sve_fnmls(llvm::Value* acc, llvm::Value* lhs, llvm::Value* rhs) + { + const auto fixed_type = llvm::cast(acc->getType()); + const auto vacc = to_sve_vector(acc); + const auto vlhs = to_sve_vector(lhs); + const auto vrhs = to_sve_vector(rhs); + const auto result = m_ir->CreateIntrinsic(llvm::Intrinsic::aarch64_sve_fnmls, {vacc->getType()}, {sve_ptrue(fixed_type), vacc, vlhs, vrhs}); + + return from_sve_vector(result, fixed_type); + } template auto addp(T1 a, T2 b) diff --git a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp index 5af63c4b90..10be1f28f2 100644 --- a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp @@ -8220,10 +8220,26 @@ public: if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate) { +#ifdef ARCH_ARM64 + if (m_use_sve2_128) + { + const auto ca = eval(clamp_smax(a)); + const auto cb = eval(clamp_smax(b)); + return value(sve_fnmls(c.value, ca.value, cb.value)); + } +#endif + return fma32x4(clamp_smax(a), clamp_smax(b), eval(-c)); } else { +#ifdef ARCH_ARM64 + if (m_use_sve2_128) + { + return value(sve_fnmls(c.value, a.value, b.value)); + } +#endif + return fma32x4(a, b, eval(-c)); } }); diff --git a/rpcs3/util/sysinfo.cpp b/rpcs3/util/sysinfo.cpp index 799d85aaca..b0be2edfdc 100755 --- a/rpcs3/util/sysinfo.cpp +++ b/rpcs3/util/sysinfo.cpp @@ -6,6 +6,7 @@ #if defined(ARCH_ARM64) #include "Emu/CPU/Backends/AArch64/AArch64Common.h" +#include #endif #ifdef _WIN32 #include "windows.h" @@ -461,6 +462,19 @@ bool utils::has_sve2() return g_value; } +#if defined(_MSC_VER) +#define sve_func +#else +#define sve_func __attribute__((__target__("+sve"))) +#endif + +// svcntb returns sve length in bytes, our function retuns length in bits +sve_func int utils::sve_length() +{ + static const int g_value = static_cast(svcntb() * 8); + return g_value; +} + #endif std::string utils::get_cpu_brand() diff --git a/rpcs3/util/sysinfo.hpp b/rpcs3/util/sysinfo.hpp index 491c54b088..fc1655d997 100755 --- a/rpcs3/util/sysinfo.hpp +++ b/rpcs3/util/sysinfo.hpp @@ -64,6 +64,8 @@ namespace utils bool has_sve(); bool has_sve2(); + + int sve_length(); #endif std::string get_cpu_brand();