From 35f65c224483b7a57092c56690fffd975d74e690 Mon Sep 17 00:00:00 2001 From: Whatcookie Date: Tue, 26 May 2026 17:26:58 -0400 Subject: [PATCH] SPU LLVM: Add ARM checksum UABA path --- rpcs3/Emu/Cell/SPULLVMRecompiler.cpp | 102 +++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp index a417246589..be7fb77095 100644 --- a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp @@ -1835,15 +1835,18 @@ public: llvm::Value* starta_pc = m_ir->CreateAnd(get_pc(starta), 0x3fffc); llvm::Value* data_addr = _ptr(m_lsptr, starta_pc); +#ifndef ARCH_ARM64 llvm::Value* acc0 = nullptr; llvm::Value* acc1 = nullptr; bool toggle = true; +#endif // Use a 512bit simple checksum to verify integrity if size is atleast 512b * 3 // This code uses a 512bit vector for all hardware to ensure behavior matches. // The checksum path is still faster even on narrow hardware. if ((end - starta) >= 192 && !g_cfg.core.precise_spu_verification) { +#ifndef ARCH_ARM64 for (u32 j = starta; j < end; j += 64) { int indices[16]; @@ -1928,6 +1931,105 @@ public: // Compare result with zero const auto cond = m_ir->CreateICmpNE(elem, m_ir->getInt64(0)); m_ir->CreateCondBr(cond, label_diff, label_body, m_md_unlikely); +#else + // + // + // + + const auto acc_init = ConstantAggregateZero::get(get_type()); + llvm::Value* checksum_parts[4] = {acc_init, acc_init, acc_init, acc_init}; + u32 checksum[16] = {0}; + + for (u32 j = starta; j < end; j += 64) + { + llvm::Value* vls[4] = {}; + u32 words[16] = {}; + bool any_data = false; + + for (u32 part = 0; part < 4; part++) + { + int indices[4]; + bool holes = false; + bool data = false; + + for (u32 i = 0; i < 4; i++) + { + const u32 k = j + (part * 4 + i) * 4; + + if (k < start || k >= end || !func.data[(k - start) / 4]) + { + indices[i] = 4; + holes = true; + } + else + { + indices[i] = i; + data = true; + words[part * 4 + i] = func.data[(k - start) / 4]; + } + } + + if (!data) + { + vls[part] = acc_init; + continue; + } + + any_data = true; + + // Load unaligned code block from LS + vls[part] = m_ir->CreateAlignedLoad(get_type(), _ptr(data_addr, j + part * 16 - starta), llvm::MaybeAlign{4}); + + // Mask if necessary + if (holes) + { + vls[part] = m_ir->CreateShuffleVector(vls[part], acc_init, llvm::ArrayRef(indices, 4)); + } + } + + if (!any_data) + { + // Skip full-sized holes + continue; + } + + // Keep the checksum logically 512-bit wide while using four NEON vectors. + // The add/uabd pattern is intended to select UABA for the difference lanes. + checksum_parts[0] = m_ir->CreateAdd(checksum_parts[0], vls[0]); + checksum_parts[1] = m_ir->CreateAdd(checksum_parts[1], m_ir->CreateCall(get_intrinsic(llvm::Intrinsic::aarch64_neon_uabd), {vls[0], vls[1]})); + checksum_parts[2] = m_ir->CreateAdd(checksum_parts[2], vls[2]); + checksum_parts[3] = m_ir->CreateAdd(checksum_parts[3], m_ir->CreateCall(get_intrinsic(llvm::Intrinsic::aarch64_neon_uabd), {vls[2], vls[3]})); + + for (u32 i = 0; i < 4; i++) + { + checksum[i] += words[i]; + checksum[4 + i] += words[i] > words[4 + i] ? words[i] - words[4 + i] : words[4 + i] - words[i]; + checksum[8 + i] += words[8 + i]; + checksum[12 + i] += words[8 + i] > words[12 + i] ? words[8 + i] - words[12 + i] : words[12 + i] - words[8 + i]; + } + + check_iterations++; + } + + llvm::Value* elem = nullptr; + + for (u32 part = 0; part < 4; part++) + { + auto* const_vector = ConstantDataVector::get(m_context, llvm::ArrayRef(checksum + part * 4, 4)); + llvm::Value* acc = m_ir->CreateXor(checksum_parts[part], const_vector); + acc = m_ir->CreateBitCast(acc, get_type()); + + for (u32 i = 0; i < 2; i++) + { + const auto lane = m_ir->CreateExtractElement(acc, i); + elem = elem ? m_ir->CreateOr(elem, lane) : lane; + } + } + + // Compare result with zero + const auto cond = m_ir->CreateICmpNE(elem, m_ir->getInt64(0)); + m_ir->CreateCondBr(cond, label_diff, label_body, m_md_unlikely); +#endif } #ifdef ARCH_ARM64 else