SPU LLVM: Add ARM checksum UABA path

This commit is contained in:
Whatcookie 2026-05-26 17:26:58 -04:00 committed by Elad
parent ed145540a2
commit 35f65c2244

View File

@ -1835,15 +1835,18 @@ public:
llvm::Value* starta_pc = m_ir->CreateAnd(get_pc(starta), 0x3fffc); llvm::Value* starta_pc = m_ir->CreateAnd(get_pc(starta), 0x3fffc);
llvm::Value* data_addr = _ptr(m_lsptr, starta_pc); llvm::Value* data_addr = _ptr(m_lsptr, starta_pc);
#ifndef ARCH_ARM64
llvm::Value* acc0 = nullptr; llvm::Value* acc0 = nullptr;
llvm::Value* acc1 = nullptr; llvm::Value* acc1 = nullptr;
bool toggle = true; bool toggle = true;
#endif
// Use a 512bit simple checksum to verify integrity if size is atleast 512b * 3 // 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. // This code uses a 512bit vector for all hardware to ensure behavior matches.
// The checksum path is still faster even on narrow hardware. // The checksum path is still faster even on narrow hardware.
if ((end - starta) >= 192 && !g_cfg.core.precise_spu_verification) if ((end - starta) >= 192 && !g_cfg.core.precise_spu_verification)
{ {
#ifndef ARCH_ARM64
for (u32 j = starta; j < end; j += 64) for (u32 j = starta; j < end; j += 64)
{ {
int indices[16]; int indices[16];
@ -1928,6 +1931,105 @@ public:
// Compare result with zero // Compare result with zero
const auto cond = m_ir->CreateICmpNE(elem, m_ir->getInt64(0)); const auto cond = m_ir->CreateICmpNE(elem, m_ir->getInt64(0));
m_ir->CreateCondBr(cond, label_diff, label_body, m_md_unlikely); m_ir->CreateCondBr(cond, label_diff, label_body, m_md_unlikely);
#else
//
//
//
const auto acc_init = ConstantAggregateZero::get(get_type<u32[4]>());
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<u32[4]>(), _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<u32[4]>(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<u32[4]>(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<u64[2]>());
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 #ifdef ARCH_ARM64
else else