SPU LLVM: Use 128-bit SVE2 for FMS

This commit is contained in:
Malcolm 2026-06-02 22:47:39 -04:00 committed by Ani
parent 9a2243bfcb
commit 4a92d96cf4
5 changed files with 85 additions and 0 deletions

View File

@ -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
}

View File

@ -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 <typename T1, typename T2, typename T3>
result.value = m_ir->CreateCall(get_intrinsic<u32[4]>(llvm::Intrinsic::aarch64_neon_umull), {data0, data1});
return result;
}
llvm::Value* to_sve_vector(llvm::Value* value)
{
if (llvm::isa<llvm::ScalableVectorType>(value->getType()))
{
return value;
}
const auto fixed_type = llvm::cast<llvm::FixedVectorType>(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<llvm::FixedVectorType>(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 <typename T1, typename T2>
auto addp(T1 a, T2 b)

View File

@ -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<f32[4]>(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<f32[4]>(sve_fnmls(c.value, a.value, b.value));
}
#endif
return fma32x4(a, b, eval(-c));
}
});

View File

@ -6,6 +6,7 @@
#if defined(ARCH_ARM64)
#include "Emu/CPU/Backends/AArch64/AArch64Common.h"
#include <arm_sve.h>
#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<int>(svcntb() * 8);
return g_value;
}
#endif
std::string utils::get_cpu_brand()

View File

@ -64,6 +64,8 @@ namespace utils
bool has_sve();
bool has_sve2();
int sve_length();
#endif
std::string get_cpu_brand();