From 60918be948996da2a902d2232d73d0ec5c8697b3 Mon Sep 17 00:00:00 2001 From: Malcolm Date: Tue, 2 Jun 2026 23:45:05 -0400 Subject: [PATCH] SPU LLVM: Use SVE2 XAR for constant rotates --- rpcs3/Emu/CPU/CPUTranslator.h | 113 +++++++++++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 1 deletion(-) diff --git a/rpcs3/Emu/CPU/CPUTranslator.h b/rpcs3/Emu/CPU/CPUTranslator.h index 0ae5179916..f7a97fe493 100644 --- a/rpcs3/Emu/CPU/CPUTranslator.h +++ b/rpcs3/Emu/CPU/CPUTranslator.h @@ -1319,11 +1319,96 @@ struct llvm_rol llvm_expr_t a1; llvm_expr_t a2; + bool use_sve_xar = false; static_assert(llvm_value_t::is_sint || llvm_value_t::is_uint, "llvm_rol<>: invalid type"); static constexpr bool is_ok = llvm_value_t::is_sint || llvm_value_t::is_uint; +#ifdef ARCH_ARM64 + static bool get_constant_splat(llvm::Value* value, u64& result) + { + if (const auto constant = llvm::dyn_cast(value)) + { + result = constant->getZExtValue(); + return true; + } + + if (llvm::isa(value)) + { + result = 0; + return true; + } + + const auto vector_type = llvm::dyn_cast(value->getType()); + + if (!vector_type) + { + return false; + } + + const auto element_count = vector_type->getNumElements(); + const auto get_element = [&](u32 index) -> llvm::Constant* + { + if (const auto data = llvm::dyn_cast(value)) + { + return data->getElementAsConstant(index); + } + + if (const auto vector = llvm::dyn_cast(value)) + { + return vector->getAggregateElement(index); + } + + return nullptr; + }; + + const auto first_element = llvm::dyn_cast_or_null(get_element(0)); + + if (!first_element) + { + return false; + } + + result = first_element->getZExtValue(); + + for (u32 i = 1; i < element_count; i++) + { + const auto element = llvm::dyn_cast_or_null(get_element(i)); + + if (!element || element->getZExtValue() != result) + { + return false; + } + } + + return true; + } + + static llvm::Value* to_sve_vector(llvm::IRBuilder<>* ir, 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 ir->CreateInsertVector(scalable_type, llvm::UndefValue::get(scalable_type), value, ir->getInt64(0)); + } + + static llvm::Value* from_sve_vector(llvm::IRBuilder<>* ir, llvm::Value* value, llvm::FixedVectorType* fixed_type) + { + if (value->getType() == fixed_type) + { + return value; + } + + return ir->CreateExtractVector(fixed_type, value, ir->getInt64(0)); + } +#endif + llvm::Value* eval(llvm::IRBuilder<>* ir) const { const auto v1 = a1.eval(ir); @@ -1334,6 +1419,28 @@ struct llvm_rol return llvm_fshl::fold(ir, v1, v1, v2); } +#ifdef ARCH_ARM64 + u64 rotate = 0; + + if (use_sve_xar && llvm::isa(v1->getType()) && get_constant_splat(v2, rotate)) + { + constexpr u64 element_size = llvm_value_t::esize; + const u32 rotate_right = static_cast((element_size - (rotate % element_size)) % element_size); + + if (rotate_right == 0) + { + return v1; + } + + const auto fixed_type = llvm::cast(v1->getType()); + const auto data = to_sve_vector(ir, v1); + const auto zero = llvm::Constant::getNullValue(data->getType()); + const auto result = ir->CreateIntrinsic(llvm::Intrinsic::aarch64_sve_xar, {data->getType()}, {data, zero, ir->getInt32(rotate_right)}); + + return from_sve_vector(ir, result, fixed_type); + } +#endif + return ir->CreateCall(llvm_fshl::get_fshl(ir), {v1, v1, v2}); } @@ -3407,9 +3514,13 @@ public: template requires llvm_rol::is_ok - static auto rol(T&& a, U&& b) + auto rol(T&& a, U&& b) { +#ifdef ARCH_ARM64 + return llvm_rol{std::forward(a), std::forward(b), m_use_sve2_128}; +#else return llvm_rol{std::forward(a), std::forward(b)}; +#endif } template