From 3164d44952201249881c858b4dd2058e4dce5893 Mon Sep 17 00:00:00 2001 From: Malcolm Date: Wed, 3 Jun 2026 09:58:32 -0400 Subject: [PATCH] SPU LLVM: Use I8MM for GBH and GBB --- rpcs3/Emu/CPU/CPUTranslator.cpp | 5 ++ rpcs3/Emu/CPU/CPUTranslator.h | 29 ++++++++++ rpcs3/Emu/Cell/SPULLVMRecompiler.cpp | 80 ++++++++++++++++++++++++++++ rpcs3/util/sysinfo.cpp | 20 +++++++ rpcs3/util/sysinfo.hpp | 2 + 5 files changed, 136 insertions(+) diff --git a/rpcs3/Emu/CPU/CPUTranslator.cpp b/rpcs3/Emu/CPU/CPUTranslator.cpp index 4b37001c83..56eece2b36 100644 --- a/rpcs3/Emu/CPU/CPUTranslator.cpp +++ b/rpcs3/Emu/CPU/CPUTranslator.cpp @@ -208,6 +208,11 @@ void cpu_translator::initialize(llvm::LLVMContext& context, llvm::ExecutionEngin m_use_dotprod = true; } + if (utils::has_i8mm()) + { + m_use_i8mm = true; + } + if (utils::has_sve() && utils::sve_length() == 128) { m_use_sve_128 = true; diff --git a/rpcs3/Emu/CPU/CPUTranslator.h b/rpcs3/Emu/CPU/CPUTranslator.h index fdb569496c..0ae5179916 100644 --- a/rpcs3/Emu/CPU/CPUTranslator.h +++ b/rpcs3/Emu/CPU/CPUTranslator.h @@ -3121,6 +3121,9 @@ protected: // ARMv8 SDOT/UDOT bool m_use_dotprod = false; + // ARMv8.6 SMMLA/UMMLA + bool m_use_i8mm = false; + // Allow direct TBL2/TBX2 emission. bool m_use_tbl2 = true; @@ -3728,6 +3731,32 @@ template return result; } + template + value_t ummla(T1 a, T2 b, T3 c) + { + value_t result; + + const auto data0 = a.eval(m_ir); + const auto data1 = b.eval(m_ir); + const auto data2 = c.eval(m_ir); + + result.value = m_ir->CreateCall(get_intrinsic(llvm::Intrinsic::aarch64_neon_ummla), {data0, data1, data2}); + return result; + } + + template + value_t smmla(T1 a, T2 b, T3 c) + { + value_t result; + + const auto data0 = a.eval(m_ir); + const auto data1 = b.eval(m_ir); + const auto data2 = c.eval(m_ir); + + result.value = m_ir->CreateCall(get_intrinsic(llvm::Intrinsic::aarch64_neon_smmla), {data0, data1, data2}); + return result; + } + template value_t smull(T1 a, T2 b) { diff --git a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp index 262df44b70..9f4491b762 100644 --- a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp @@ -5524,6 +5524,44 @@ public: const auto a = get_vr(op.ra); #ifdef ARCH_ARM64 + if (m_use_i8mm) + { + if (match_vr(op.ra, [&](auto c, auto MP) + { + using VT = typename decltype(MP)::type; + + if (auto [ok, x] = match_expr(c, sext(match]>())); ok) + { + const auto zeroes = splat(0); + const auto es = zshuffle(bitcast(a), 16, 16, 16, 16, 16, 16, 16, 16, 0, 2, 4, 6, 8, 10, 12, 14); + + set_vr(op.rt, smmla(zeroes, es, build( + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + -0x01, -0x02, -0x04, -0x08, + -0x10, -0x20, -0x40, -0x80 + ))); + return true; + } + return false; + })) + { + return; + } + + const auto zeroes = splat(0); + const auto masked = a & 0x01; + const auto es = zshuffle(bitcast(masked), 16, 16, 16, 16, 16, 16, 16, 16, 0, 2, 4, 6, 8, 10, 12, 14); + + set_vr(op.rt, ummla(zeroes, es, build( + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x04, 0x08, + 0x10, 0x20, 0x40, 0x80 + ))); + return; + } + // Use dot product instructions with special values to shift then sum results into the preferred slot if (m_use_dotprod) { @@ -5579,6 +5617,48 @@ public: const auto a = get_vr(op.ra); #ifdef ARCH_ARM64 + if (m_use_i8mm) + { + if (match_vr(op.ra, [&](auto c, auto MP) + { + using VT = typename decltype(MP)::type; + + if (auto [ok, x] = match_expr(c, sext(match]>())); ok) + { + const auto zeroes = splat(0); + + const auto extracted = smmla(zeroes, a, build( + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + -0x01, -0x02, -0x04, -0x08, + -0x10, -0x20, -0x40, -0x80 + )); + + const auto es = zshuffle(bitcast(extracted), 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 4, 12, 16, 16); + set_vr(op.rt, bitcast(es)); + return true; + } + return false; + })) + { + return; + } + + const auto zeroes = splat(0); + const auto masked = a & 0x01; + + const auto extracted = ummla(zeroes, masked, build( + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x04, 0x08, + 0x10, 0x20, 0x40, 0x80 + )); + + const auto es = zshuffle(bitcast(extracted), 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 4, 12, 16, 16); + set_vr(op.rt, bitcast(es)); + return; + } + // Use dot product instructions with special values to shift then sum results into the preferred slot if (m_use_dotprod) { diff --git a/rpcs3/util/sysinfo.cpp b/rpcs3/util/sysinfo.cpp index 7748e41a09..fb6dd6d7f7 100755 --- a/rpcs3/util/sysinfo.cpp +++ b/rpcs3/util/sysinfo.cpp @@ -426,6 +426,26 @@ bool utils::has_dotprod() return g_value; } +bool utils::has_i8mm() +{ + static const bool g_value = []() -> bool + { +#if defined(__linux__) + return (getauxval(AT_HWCAP2) & HWCAP2_I8MM) != 0; +#elif defined(__APPLE__) + int val = 0; + size_t len = sizeof(val); + sysctlbyname("hw.optional.arm.FEAT_I8MM", &val, &len, nullptr, 0); + return val != 0; +#elif defined(_WIN32) + return IsProcessorFeaturePresent(PF_ARM_V82_I8MM_INSTRUCTIONS_AVAILABLE) != 0; +#else + return false; +#endif + }(); + return g_value; +} + bool utils::has_sve() { static const bool g_value = []() -> bool diff --git a/rpcs3/util/sysinfo.hpp b/rpcs3/util/sysinfo.hpp index fc1655d997..dfe781e17a 100755 --- a/rpcs3/util/sysinfo.hpp +++ b/rpcs3/util/sysinfo.hpp @@ -61,6 +61,8 @@ namespace utils bool has_dotprod(); + bool has_i8mm(); + bool has_sve(); bool has_sve2();